GCC Code Coverage Report


Directory: ./
File: storage/innobase/handler/handler0alter.cc
Date: 2022-11-26 14:12:44
Exec Total Coverage
Lines: 4172 4776 87.4%
Branches: 3842 6423 59.8%

Line Branch Exec Source
1 /*****************************************************************************
2
3 Copyright (c) 2005, 2022, Oracle and/or its affiliates.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25 *****************************************************************************/
26
27 /** @file handler/handler0alter.cc
28 Smart ALTER TABLE
29 *******************************************************/
30
31 /* Include necessary SQL headers */
32 #include <assert.h>
33 #include <current_thd.h>
34 #include <debug_sync.h>
35 #include <key_spec.h>
36 #include <log.h>
37 #include <my_bit.h>
38 #include <mysql/plugin.h>
39 #include <sql_class.h>
40 #include <sql_lex.h>
41 #include <sql_table.h>
42 #include <sql_thd_internal_api.h>
43 #include <sys/types.h>
44 #include "ha_prototypes.h"
45
46 #include "dd/cache/dictionary_client.h"
47 #include "dd/dd.h"
48 #include "dd/dictionary.h"
49 #include "dd/impl/properties_impl.h"
50 #include "dd/impl/types/column_impl.h"
51 #include "dd/properties.h"
52 #include "dd/types/column.h"
53 #include "dd/types/column_type_element.h"
54 #include "dd/types/index.h"
55 #include "dd/types/index_element.h"
56 #include "dd/types/partition.h"
57 #include "dd/types/partition_index.h"
58 #include "dd/types/table.h"
59 #include "dd/types/tablespace_file.h"
60 #include "dd_table_share.h"
61
62 #include "btr0sea.h"
63 #include "dict0crea.h"
64 #include "dict0dd.h"
65 #include "dict0dict.h"
66 #include "dict0inst.h" //Instant DDL
67 #include "dict0priv.h"
68 #include "dict0stats.h"
69 #include "dict0stats_bg.h"
70 #include "fsp0sysspace.h"
71 #include "fts0plugin.h"
72 #include "fts0priv.h"
73 #include "ha_innodb.h"
74 #include "ha_innopart.h"
75 #include "ha_prototypes.h"
76 #include "handler0alter.h"
77 #include "lex_string.h"
78 #include "log0buf.h"
79 #include "log0chkp.h"
80
81 #include "my_dbug.h"
82 #include "my_io.h"
83
84 #include "clone0api.h"
85 #include "ddl0ddl.h"
86 #include "dict0dd.h"
87 #include "fts0plugin.h"
88 #include "fts0priv.h"
89 #include "handler0alter.h"
90 #include "lock0lock.h"
91 #include "pars0pars.h"
92 #include "partition_info.h"
93 #include "rem0types.h"
94 #include "row0ins.h"
95 #include "row0log.h"
96 #include "row0sel.h"
97 #include "sql/create_field.h"
98 #include "srv0mon.h"
99 #include "trx0roll.h"
100 #include "trx0trx.h"
101 #include "ut0new.h"
102 #include "ut0stage.h"
103
104 /* For supporting Native InnoDB Partitioning. */
105 #include "ha_innopart.h"
106 #include "partition_info.h"
107
108 extern char server_uuid[UUID_LENGTH + 1];
109
110 /** Function to convert the Instant_Type to a comparable int */
111 488343 inline uint16_t instant_type_to_int(Instant_Type type) {
112 488343 return (static_cast<typename std::underlying_type<Log_Type>::type>(type));
113 }
114
115 /** Operations for creating secondary indexes (no rebuild needed) */
116 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ONLINE_CREATE =
117 Alter_inplace_info::ADD_INDEX | Alter_inplace_info::ADD_UNIQUE_INDEX |
118 Alter_inplace_info::ADD_SPATIAL_INDEX;
119
120 /** Operations for rebuilding a table in place */
121 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_REBUILD =
122 Alter_inplace_info::ADD_PK_INDEX | Alter_inplace_info::DROP_PK_INDEX |
123 Alter_inplace_info::CHANGE_CREATE_OPTION
124 /* CHANGE_CREATE_OPTION needs to check innobase_need_rebuild() */
125 | Alter_inplace_info::ALTER_COLUMN_NULLABLE |
126 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE |
127 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
128 Alter_inplace_info::DROP_STORED_COLUMN |
129 Alter_inplace_info::ADD_STORED_BASE_COLUMN
130 /* ADD_STORED_BASE_COLUMN needs to check innobase_need_rebuild() */
131 | Alter_inplace_info::RECREATE_TABLE;
132
133 /** Operations that require changes to data */
134 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_DATA =
135 INNOBASE_ONLINE_CREATE | INNOBASE_ALTER_REBUILD;
136
137 /** Operations for altering a table that InnoDB does not care about */
138 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_INPLACE_IGNORE =
139 Alter_inplace_info::ALTER_COLUMN_DEFAULT |
140 Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT |
141 Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE |
142 Alter_inplace_info::ALTER_RENAME | Alter_inplace_info::CHANGE_INDEX_OPTION |
143 Alter_inplace_info::ADD_CHECK_CONSTRAINT |
144 Alter_inplace_info::DROP_CHECK_CONSTRAINT |
145 Alter_inplace_info::SUSPEND_CHECK_CONSTRAINT |
146 Alter_inplace_info::ALTER_COLUMN_VISIBILITY;
147
148 /** Operation allowed with ALGORITHM=INSTANT */
149 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_INSTANT_ALLOWED =
150 Alter_inplace_info::ALTER_COLUMN_NAME |
151 Alter_inplace_info::ADD_VIRTUAL_COLUMN |
152 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
153 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER |
154 Alter_inplace_info::ADD_STORED_BASE_COLUMN |
155 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
156 Alter_inplace_info::DROP_STORED_COLUMN;
157
158 /** Operations on foreign key definitions (changing the schema only) */
159 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_FOREIGN_OPERATIONS =
160 Alter_inplace_info::DROP_FOREIGN_KEY | Alter_inplace_info::ADD_FOREIGN_KEY;
161
162 /** Operations that InnoDB cares about and can perform without rebuild */
163 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_NOREBUILD =
164 INNOBASE_ONLINE_CREATE | INNOBASE_FOREIGN_OPERATIONS |
165 Alter_inplace_info::DROP_INDEX | Alter_inplace_info::DROP_UNIQUE_INDEX |
166 Alter_inplace_info::RENAME_INDEX | Alter_inplace_info::ALTER_COLUMN_NAME |
167 Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
168 Alter_inplace_info::ALTER_INDEX_COMMENT |
169 Alter_inplace_info::ADD_VIRTUAL_COLUMN |
170 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
171 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER |
172 Alter_inplace_info::ALTER_COLUMN_INDEX_LENGTH;
173
174 struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx {
175 /** Dummy query graph */
176 que_thr_t *thr;
177 /** The prebuilt struct of the creating instance */
178 row_prebuilt_t *prebuilt;
179 /** InnoDB indexes being created */
180 dict_index_t **add_index;
181 /** MySQL key numbers for the InnoDB indexes that are being created */
182 const ulint *add_key_numbers;
183 /** number of InnoDB indexes being created */
184 ulint num_to_add_index;
185 /** InnoDB indexes being dropped */
186 dict_index_t **drop_index;
187 /** number of InnoDB indexes being dropped */
188 const ulint num_to_drop_index;
189 /** InnoDB indexes being renamed */
190 dict_index_t **rename;
191 /** number of InnoDB indexes being renamed */
192 const ulint num_to_rename;
193 /** InnoDB foreign key constraints being dropped */
194 dict_foreign_t **drop_fk;
195 /** number of InnoDB foreign key constraints being dropped */
196 const ulint num_to_drop_fk;
197 /** InnoDB foreign key constraints being added */
198 dict_foreign_t **add_fk;
199 /** number of InnoDB foreign key constraints being dropped */
200 const ulint num_to_add_fk;
201 /** whether to create the indexes online */
202 bool online;
203 /** memory heap */
204 mem_heap_t *heap;
205 /** dictionary transaction */
206 trx_t *trx;
207 /** original table (if rebuilt, differs from indexed_table) */
208 dict_table_t *old_table;
209 /** table where the indexes are being created or dropped */
210 dict_table_t *new_table;
211 /** mapping of old column numbers to new ones, or NULL */
212 const ulint *col_map;
213 /** new column names, or NULL if nothing was renamed */
214 const char **col_names;
215 /** added AUTO_INCREMENT column position, or ULINT_UNDEFINED */
216 const ulint add_autoinc;
217 /** default values of ADD COLUMN, or NULL */
218 const dtuple_t *add_cols;
219 /** autoinc sequence to use */
220 ddl::Sequence sequence;
221 /** maximum auto-increment value */
222 ulonglong max_autoinc;
223 /** temporary table name to use for old table when renaming tables */
224 const char *tmp_name;
225 /** whether the order of the clustered index is unchanged */
226 bool skip_pk_sort;
227 /** virtual columns to be added */
228 dict_v_col_t *add_vcol;
229 const char **add_vcol_name;
230 /** virtual columns to be dropped */
231 dict_v_col_t *drop_vcol;
232 const char **drop_vcol_name;
233 /** ALTER TABLE stage progress recorder */
234 Alter_stage *m_stage;
235 /** FTS AUX Tables to drop */
236 aux_name_vec_t *fts_drop_aux_vec;
237
238 47702 ha_innobase_inplace_ctx(row_prebuilt_t *prebuilt_arg, dict_index_t **drop_arg,
239 ulint num_to_drop_arg, dict_index_t **rename_arg,
240 ulint num_to_rename_arg, dict_foreign_t **drop_fk_arg,
241 ulint num_to_drop_fk_arg, dict_foreign_t **add_fk_arg,
242 ulint num_to_add_fk_arg, bool online_arg,
243 mem_heap_t *heap_arg, dict_table_t *new_table_arg,
244 const char **col_names_arg, ulint add_autoinc_arg,
245 ulonglong autoinc_col_min_value_arg,
246 ulonglong autoinc_col_max_value_arg)
247 47702 : inplace_alter_handler_ctx(),
248 47702 prebuilt(prebuilt_arg),
249 47702 add_index(nullptr),
250 47702 add_key_numbers(nullptr),
251 47702 num_to_add_index(0),
252 47702 drop_index(drop_arg),
253 47702 num_to_drop_index(num_to_drop_arg),
254 47702 rename(rename_arg),
255 47702 num_to_rename(num_to_rename_arg),
256 47702 drop_fk(drop_fk_arg),
257 47702 num_to_drop_fk(num_to_drop_fk_arg),
258 47702 add_fk(add_fk_arg),
259 47702 num_to_add_fk(num_to_add_fk_arg),
260 47702 online(online_arg),
261 47702 heap(heap_arg),
262 47702 trx(nullptr),
263 47702 old_table(prebuilt_arg->table),
264 47702 new_table(new_table_arg),
265 47702 col_map(nullptr),
266 47702 col_names(col_names_arg),
267 47702 add_autoinc(add_autoinc_arg),
268 47702 add_cols(nullptr),
269 47702 sequence(prebuilt->trx->mysql_thd, autoinc_col_min_value_arg,
270 autoinc_col_max_value_arg),
271 47702 max_autoinc(0),
272 47702 tmp_name(nullptr),
273 47702 skip_pk_sort(false),
274 47702 add_vcol(nullptr),
275 47702 add_vcol_name(nullptr),
276 47702 drop_vcol(nullptr),
277 47702 drop_vcol_name(nullptr),
278 47702 m_stage(nullptr),
279 47702 fts_drop_aux_vec(nullptr) {
280 #ifdef UNIV_DEBUG
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47702 times.
47702 for (ulint i = 0; i < num_to_add_index; i++) {
282 ut_ad(!add_index[i]->to_be_dropped);
283 }
284
2/2
✓ Branch 0 taken 1318 times.
✓ Branch 1 taken 47702 times.
49020 for (ulint i = 0; i < num_to_drop_index; i++) {
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1318 times.
1318 ut_ad(drop_index[i]->to_be_dropped);
286 }
287 #endif /* UNIV_DEBUG */
288
289
1/2
✓ Branch 0 taken 47702 times.
✗ Branch 1 not taken.
47702 thr = pars_complete_graph_for_exec(nullptr, prebuilt->trx, heap, prebuilt);
290 47702 }
291
292 94196 ~ha_innobase_inplace_ctx() override {
293
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 46990 times.
94196 if (fts_drop_aux_vec != nullptr) {
294 216 fts_free_aux_names(fts_drop_aux_vec);
295
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
216 delete fts_drop_aux_vec;
296 }
297 94196 ut::delete_(m_stage);
298 94196 mem_heap_free(heap);
299 }
300
301 /** Determine if the table will be rebuilt.
302 @return whether the table will be rebuilt */
303 886668 bool need_rebuild() const { return (old_table != new_table); }
304
305 /** Set shared data between the passed in handler context
306 and current context.
307 @param[in] ctx handler context */
308 2360 void set_shared_data(const inplace_alter_handler_ctx *ctx) override {
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2360 times.
2360 ut_ad(ctx != nullptr);
310
2/2
✓ Branch 0 taken 2357 times.
✓ Branch 1 taken 3 times.
2360 if (add_autoinc == ULINT_UNDEFINED) {
311 2357 return;
312 }
313 3 const ha_innobase_inplace_ctx *ha_ctx =
314 static_cast<const ha_innobase_inplace_ctx *>(ctx);
315
316 /* In InnoDB table, if it's adding AUTOINC column,
317 the sequence value should be shared among contexts */
318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(ha_ctx->add_autoinc != ULINT_UNDEFINED);
319 3 sequence = ha_ctx->sequence;
320 }
321
322 private:
323 // Disable copying
324 ha_innobase_inplace_ctx(const ha_innobase_inplace_ctx &);
325 ha_innobase_inplace_ctx &operator=(const ha_innobase_inplace_ctx &);
326 };
327
328 /** Structure to remember table information for updating DD */
329 struct alter_table_old_info_t {
330 /** Constructor */
331 56562 alter_table_old_info_t() : m_discarded(), m_fts_doc_id(), m_rebuild() {}
332
333 /** If old table is discarded one */
334 bool m_discarded;
335
336 /** If old table has FTS DOC ID */
337 bool m_fts_doc_id;
338
339 /** If this ATLER TABLE requires rebuild */
340 bool m_rebuild;
341
342 /** Update the old table information
343 @param[in] old_table Old InnoDB table object
344 @param[in] rebuild True if rebuild is necessary */
345 47397 void update(const dict_table_t *old_table, bool rebuild) {
346 47397 m_discarded = dict_table_is_discarded(old_table);
347 47397 m_fts_doc_id = DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID);
348 47397 m_rebuild = rebuild;
349 47397 }
350 };
351
352 /* Report an InnoDB error to the client by invoking my_error(). */
353 115 static UNIV_COLD void my_error_innodb(
354 dberr_t error, /*!< in: InnoDB error code */
355 const char *table, /*!< in: table name */
356 uint32_t flags) /*!< in: table flags */
357 {
358
10/20
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 23 times.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 49 times.
115 switch (error) {
359 case DB_MISSING_HISTORY:
360 my_error(ER_TABLE_DEF_CHANGED, MYF(0));
361 break;
362 case DB_RECORD_NOT_FOUND:
363 my_error(ER_KEY_NOT_FOUND, MYF(0), table);
364 break;
365 1 case DB_DEADLOCK:
366 1 my_error(ER_LOCK_DEADLOCK, MYF(0));
367 1 break;
368 case DB_LOCK_WAIT_TIMEOUT:
369 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
370 break;
371 8 case DB_INTERRUPTED:
372 8 my_error(ER_QUERY_INTERRUPTED, MYF(0));
373 8 break;
374 16 case DB_OUT_OF_MEMORY:
375 16 my_error(ER_OUT_OF_RESOURCES, MYF(0));
376 16 break;
377 4 case DB_OUT_OF_FILE_SPACE:
378 4 my_error(ER_RECORD_FILE_FULL, MYF(0), table);
379 4 break;
380 case DB_OUT_OF_DISK_SPACE:
381 my_error(ER_DISK_FULL_NOWAIT, MYF(0), table);
382 break;
383 1 case DB_TEMP_FILE_WRITE_FAIL:
384 1 my_error(ER_TEMP_FILE_WRITE_FAILURE, MYF(0));
385 1 break;
386 case DB_TOO_BIG_INDEX_COL:
387 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
388 DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
389 break;
390 2 case DB_TOO_MANY_CONCURRENT_TRXS:
391 2 my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
392 2 break;
393 case DB_LOCK_TABLE_FULL:
394 my_error(ER_LOCK_TABLE_FULL, MYF(0));
395 break;
396 case DB_UNDO_RECORD_TOO_BIG:
397 my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
398 break;
399 case DB_CORRUPTION:
400 my_error(ER_NOT_KEYFILE, MYF(0), table);
401 break;
402 23 case DB_TOO_BIG_RECORD:
403 /* We limit max record size to 16k for 64k page size. */
404 23 my_error(ER_TOO_BIG_ROWSIZE, MYF(0),
405
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 srv_page_size == UNIV_PAGE_SIZE_MAX
406 ? REC_MAX_DATA_SIZE - 1
407 23 : page_get_free_space_of_empty(flags & DICT_TF_COMPACT) / 2);
408 23 break;
409 8 case DB_INVALID_NULL:
410 /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
411 8 my_error(ER_INVALID_USE_OF_NULL, MYF(0));
412 8 break;
413 case DB_CANT_CREATE_GEOMETRY_OBJECT:
414 my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
415 break;
416 3 case DB_TABLESPACE_EXISTS:
417 3 my_error(ER_TABLESPACE_EXISTS, MYF(0), table);
418 3 break;
419
420 #ifdef UNIV_DEBUG
421 case DB_SUCCESS:
422 case DB_DUPLICATE_KEY:
423 case DB_ONLINE_LOG_TOO_BIG:
424 /* These codes should not be passed here. */
425 ut_error;
426 #endif /* UNIV_DEBUG */
427 49 default:
428 49 my_error(ER_GET_ERRNO, MYF(0), error, "InnoDB error");
429 49 break;
430 }
431 115 }
432
433 /** Determine if fulltext indexes exist in a given table.
434 @param table MySQL table
435 @return whether fulltext indexes exist on the table */
436 100316 static bool innobase_fulltext_exist(const TABLE *table) {
437
2/2
✓ Branch 0 taken 117285 times.
✓ Branch 1 taken 99080 times.
216365 for (uint i = 0; i < table->s->keys; i++) {
438
2/2
✓ Branch 0 taken 1236 times.
✓ Branch 1 taken 116049 times.
117285 if (table->key_info[i].flags & HA_FULLTEXT) {
439 1236 return (true);
440 }
441 }
442
443 99080 return (false);
444 }
445
446 /** Determine if spatial indexes exist in a given table.
447 @param table MySQL table
448 @return whether spatial indexes exist on the table */
449 26679 static bool innobase_spatial_exist(const TABLE *table) {
450
2/2
✓ Branch 0 taken 27446 times.
✓ Branch 1 taken 26641 times.
54087 for (uint i = 0; i < table->s->keys; i++) {
451
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 27408 times.
27446 if (table->key_info[i].flags & HA_SPATIAL) {
452 38 return (true);
453 }
454 }
455
456 26641 return (false);
457 }
458
459 /** Get col in new table def of renamed column.
460 @param[in] ha_alter_info inplace alter info
461 @param[in] old_dd_column column in old table
462 @param[in] new_dd_tab new table definition
463 @return column if renamed, NULL otherwise */
464 static dd::Column *get_renamed_col(const Alter_inplace_info *ha_alter_info,
465 const dd::Column *old_dd_column,
466 const dd::Table *new_dd_tab) {
467 List_iterator_fast<Create_field> cf_it(
468 ha_alter_info->alter_info->create_list);
469 cf_it.rewind();
470 Create_field *cf;
471 while ((cf = cf_it++) != nullptr) {
472 if (cf->field && cf->field->is_flag_set(FIELD_IS_RENAMED) &&
473 strcmp(cf->change, old_dd_column->name().c_str()) == 0) {
474 /* This column is being renamed */
475 return (const_cast<dd::Column *>(
476 dd_find_column(&new_dd_tab->table(), cf->field_name)));
477 }
478 }
479
480 return nullptr;
481 }
482
483 /** Copy metadata of dd::Table and dd::Columns from old table to new table.
484 This is done during inplce alter table when table is not rebuilt.
485 @param[in] ha_alter_info inplace alter info
486 @param[in] old_dd_tab old table definition
487 @param[in,out] new_dd_tab new table definition */
488 16347 static void dd_inplace_alter_copy_instant_metadata(
489 const Alter_inplace_info *ha_alter_info, const dd::Table *old_dd_tab,
490 dd::Table *new_dd_tab) {
491
2/2
✓ Branch 0 taken 16308 times.
✓ Branch 1 taken 39 times.
16347 if (!dd_table_has_row_versions(*old_dd_tab)) {
492 16308 return;
493 }
494
495 /* Copy col phy pos from old DD table to new DD table */
496
6/10
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 375 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 375 times.
✓ Branch 9 taken 39 times.
414 for (auto old_dd_column : old_dd_tab->columns()) {
497 375 const char *s = dd_column_key_strings[DD_INSTANT_VERSION_DROPPED];
498
5/8
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 375 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 375 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
✓ Branch 7 taken 313 times.
375 if (old_dd_column->se_private_data().exists(s)) {
499 62 uint32_t v_dropped = UINT32_UNDEFINED;
500
3/6
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 old_dd_column->se_private_data().get(s, &v_dropped);
501
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 if (v_dropped > 0) {
502 /* Dropped column will be copied after the loop. Skip for now. */
503 62 continue;
504 }
505 }
506
507 /* Get corresponding dd::column in new table */
508 dd::Column *new_dd_column = const_cast<dd::Column *>(
509
2/4
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 313 times.
✗ Branch 3 not taken.
313 dd_find_column(new_dd_tab, old_dd_column->name().c_str()));
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
313 if (new_dd_column == nullptr) {
511 /* This column might have been renamed */
512 new_dd_column = get_renamed_col(ha_alter_info, old_dd_column, new_dd_tab);
513 }
514
515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
313 if (new_dd_column == nullptr) {
516 /* This column must have been dropped */
517 continue;
518 }
519
520
2/4
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 313 times.
313 if (new_dd_column->is_virtual()) {
521 continue;
522 }
523
524 1356 auto fn = [&](const char *s, auto &value) {
525
4/6
✓ Branch 0 taken 678 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 678 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 413 times.
✓ Branch 5 taken 265 times.
1356 if (old_dd_column->se_private_data().exists(s)) {
526
2/4
✓ Branch 0 taken 413 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 413 times.
✗ Branch 3 not taken.
826 old_dd_column->se_private_data().get(s, &value);
527
2/4
✓ Branch 0 taken 413 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 413 times.
✗ Branch 3 not taken.
826 new_dd_column->se_private_data().set(s, value);
528 }
529 1669 };
530
531 /* Copy phy pos for column */
532 313 uint32_t phy_pos = UINT32_UNDEFINED;
533 313 s = dd_column_key_strings[DD_INSTANT_PHYSICAL_POS];
534
4/8
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 313 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 313 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 313 times.
313 ut_ad(old_dd_column->se_private_data().exists(s));
535
1/2
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
313 fn(s, phy_pos);
536
537 /* copy version added */
538 313 uint32_t v_added = UINT32_UNDEFINED;
539 313 s = dd_column_key_strings[DD_INSTANT_VERSION_ADDED];
540
1/2
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
313 fn(s, v_added);
541
542 /* Copy instant default values for INSTANT ADD columns */
543 313 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL];
544
5/8
✓ Branch 0 taken 313 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 313 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 313 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 13 times.
✓ Branch 7 taken 300 times.
313 if (old_dd_column->se_private_data().exists(s)) {
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 ut_ad(v_added > 0);
546 13 bool value = false;
547
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 fn(s, value);
548 } else {
549 300 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT];
550
5/8
✓ Branch 0 taken 300 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 300 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
✓ Branch 7 taken 261 times.
300 if (old_dd_column->se_private_data().exists(s)) {
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 ut_ad(v_added > 0);
552 39 dd::String_type value;
553
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 fn(s, value);
554 39 } else {
555 /* This columns is not INSTANT ADD */
556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 ut_ad(v_added == UINT32_UNDEFINED);
557 }
558 }
559 }
560
561
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 31 times.
39 if (dd_table_has_instant_drop_cols(*old_dd_tab)) {
562 /* Add INSTANT dropped column from old_dd_tab to new_dd_tab */
563 8 copy_dropped_columns(old_dd_tab, new_dd_tab, UINT32_UNDEFINED);
564 }
565 }
566
567 /** Check if virtual column in old and new table are in order, excluding
568 those dropped column. This is needed because when we drop a virtual column,
569 ALTER_VIRTUAL_COLUMN_ORDER is also turned on, so we can't decide if this
570 is a real ORDER change or just DROP COLUMN
571 @param[in] table old TABLE
572 @param[in] altered_table new TABLE
573 @param[in] ha_alter_info Structure describing changes to be done
574 by ALTER TABLE and holding data used during in-place alter.
575 @return true is all columns in order, false otherwise. */
576 37554 static bool check_v_col_in_order(const TABLE *table, const TABLE *altered_table,
577 const Alter_inplace_info *ha_alter_info) {
578 37554 ulint j = 0;
579
580 /* We don't support any adding new virtual column before
581 existed virtual column. */
582
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 36938 times.
37554 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_VIRTUAL_COLUMN) {
583 616 bool has_new = false;
584
585 List_iterator_fast<Create_field> cf_it(
586
1/2
✓ Branch 0 taken 616 times.
✗ Branch 1 not taken.
616 ha_alter_info->alter_info->create_list);
587
588 616 cf_it.rewind();
589
590
2/2
✓ Branch 0 taken 2723 times.
✓ Branch 1 taken 609 times.
3332 while (const Create_field *new_field = cf_it++) {
591
2/2
✓ Branch 0 taken 1677 times.
✓ Branch 1 taken 1046 times.
2723 if (!new_field->is_virtual_gcol()) {
592 /* We do not support add virtual col
593 before autoinc column */
594
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1675 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
1677 if (has_new && (new_field->flags & AUTO_INCREMENT_FLAG)) {
595 7 return (false);
596 }
597 1677 continue;
598 }
599
600 /* Found a new added virtual column. */
601
2/2
✓ Branch 0 taken 656 times.
✓ Branch 1 taken 390 times.
1046 if (!new_field->field) {
602 656 has_new = true;
603 656 continue;
604 }
605
606 /* If there's any old virtual column
607 after the new added virtual column,
608 order must be changed. */
609
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 383 times.
390 if (has_new) {
610 7 return (false);
611 }
612 2716 }
613 }
614
615 /* directly return true if ALTER_VIRTUAL_COLUMN_ORDER is not on */
616
2/2
✓ Branch 0 taken 37504 times.
✓ Branch 1 taken 43 times.
37547 if (!(ha_alter_info->handler_flags &
617 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER)) {
618 37504 return (true);
619 }
620
621
2/2
✓ Branch 0 taken 356 times.
✓ Branch 1 taken 42 times.
398 for (ulint i = 0; i < table->s->fields; i++) {
622 356 Field *field = table->s->field[i];
623 356 bool dropped = false;
624
625
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 152 times.
356 if (field->stored_in_db) {
626 204 continue;
627 }
628
629
3/6
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 152 times.
152 ut_ad(innobase_is_v_fld(field));
630
631 /* Check if this column is in drop list */
632
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 109 times.
332 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
633
4/4
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 180 times.
389 if (drop->type == Alter_drop::COLUMN &&
634
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 123 times.
166 my_strcasecmp(system_charset_info, field->field_name, drop->name) ==
635 0) {
636 43 dropped = true;
637 43 break;
638 }
639 }
640
641
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 109 times.
152 if (dropped) {
642 43 continue;
643 }
644
645 /* Now check if the next virtual column in altered table
646 matches this column */
647
1/2
✓ Branch 0 taken 284 times.
✗ Branch 1 not taken.
284 while (j < altered_table->s->fields) {
648 284 Field *new_field = altered_table->s->field[j];
649
650
2/2
✓ Branch 0 taken 175 times.
✓ Branch 1 taken 109 times.
284 if (new_field->stored_in_db) {
651 175 j++;
652 175 continue;
653 }
654
655 109 if (my_strcasecmp(system_charset_info, field->field_name,
656
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 108 times.
109 new_field->field_name) != 0) {
657 /* different column */
658 1 return (false);
659 } else {
660 108 j++;
661 108 break;
662 }
663 }
664
665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 108 times.
108 if (j > altered_table->s->fields) {
666 /* there should not be less column in new table
667 without them being in drop list */
668 ut_d(ut_error);
669 ut_o(return (false));
670 }
671 }
672
673 42 return (true);
674 }
675
676 /** Drop the statistics for a specified table, and mark it as discard
677 after DDL
678 @param[in,out] thd THD object
679 @param[in,out] table InnoDB table object */
680 2321 void innobase_discard_table(THD *thd, dict_table_t *table) {
681 char errstr[ERROR_STR_LENGTH];
682
2/4
✓ Branch 0 taken 2321 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2321 times.
2321 if (dict_stats_drop_table(table->name.m_name, errstr, sizeof(errstr)) !=
683 DB_SUCCESS) {
684 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
685 "Deleting persistent statistics"
686 " for table '%s' in"
687 " InnoDB failed: %s",
688 table->name.m_name, errstr);
689 }
690
691 2321 table->discard_after_ddl = true;
692 2321 }
693
694 /* To check if renaming a column is ok.
695 @return true if Ok, false otherwise */
696 388 static bool ok_to_rename_column(const Alter_inplace_info *ha_alter_info,
697 const TABLE *old_table,
698 const TABLE *altered_table,
699 const dict_table_t *dict_table, bool instant,
700 bool report_error) {
701 List_iterator_fast<Create_field> cf_it(
702
1/2
✓ Branch 0 taken 388 times.
✗ Branch 1 not taken.
388 ha_alter_info->alter_info->create_list);
703
704
2/2
✓ Branch 0 taken 1251 times.
✓ Branch 1 taken 382 times.
1633 for (Field **fp = old_table->field; *fp; fp++) {
705
2/2
✓ Branch 0 taken 834 times.
✓ Branch 1 taken 417 times.
1251 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
706 834 continue;
707 }
708
709 417 const char *name = nullptr;
710
711 417 cf_it.rewind();
712
1/2
✓ Branch 0 taken 821 times.
✗ Branch 1 not taken.
821 while (const Create_field *cf = cf_it++) {
713
2/2
✓ Branch 0 taken 417 times.
✓ Branch 1 taken 404 times.
821 if (cf->field == *fp) {
714 417 name = cf->field_name;
715 417 goto check_if_ok_to_rename;
716 }
717 404 }
718
719 ut_error;
720 417 check_if_ok_to_rename:
721 /* Prohibit renaming a column from FTS_DOC_ID
722 if full-text indexes exist. */
723
1/2
✓ Branch 0 taken 417 times.
✗ Branch 1 not taken.
417 if (!my_strcasecmp(system_charset_info, (*fp)->field_name,
724
6/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 412 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 415 times.
422 FTS_DOC_ID_COL_NAME) &&
725 5 innobase_fulltext_exist(altered_table)) {
726
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (report_error) {
727 my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN, MYF(0), name);
728 }
729 2 return false;
730 }
731
732 /* Prohibit renaming a column to an internal column. */
733 415 const char *s = dict_table->col_names;
734 unsigned j;
735 /* Skip user columns.
736 MySQL should have checked these already.
737 We want to allow renaming of c1 to c2, c2 to c1. */
738
2/2
✓ Branch 0 taken 1346 times.
✓ Branch 1 taken 415 times.
1761 for (j = 0; j < old_table->s->fields; j++) {
739
3/4
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 1262 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
1346 if (!innobase_is_v_fld(old_table->field[j])) {
740 1262 s += strlen(s) + 1;
741 }
742 }
743
744
2/2
✓ Branch 0 taken 1222 times.
✓ Branch 1 taken 411 times.
1633 for (; j < dict_table->n_def; j++) {
745
3/4
✓ Branch 0 taken 1222 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1218 times.
1222 if (!my_strcasecmp(system_charset_info, name, s)) {
746
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (report_error) {
747
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_error(ER_WRONG_COLUMN_NAME, MYF(0), s);
748 }
749 4 return false;
750 }
751
752 1218 s += strlen(s) + 1;
753 }
754 }
755
756 /* If column being renamed is being referenced by any other table, don't
757 allow INSTANT in that case. */
758
2/2
✓ Branch 0 taken 318 times.
✓ Branch 1 taken 64 times.
382 if (instant) {
759
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 299 times.
318 if (!dict_table->referenced_set.empty()) {
760 List_iterator_fast<Create_field> cf_it(
761
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 ha_alter_info->alter_info->create_list);
762
763
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 for (Field **fp = old_table->field; *fp; fp++) {
764
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 19 times.
35 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
765 16 continue;
766 }
767
768 19 const char *col_name = (*fp)->field_name;
769
770 19 for (dict_foreign_set::iterator it = dict_table->referenced_set.begin();
771
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 it != dict_table->referenced_set.end(); ++it) {
772 19 dict_foreign_t *foreign = *it;
773 19 const char *r_name = foreign->referenced_col_names[0];
774
775
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 for (size_t i = 0; i < foreign->n_fields; ++i) {
776
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 if (!my_strcasecmp(system_charset_info, r_name, col_name)) {
777
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (report_error) {
778 my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
779 "ALGORITHM=INSTANT",
780 innobase_get_err_msg(
781 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME),
782 "ALGORITHM=INPLACE");
783 }
784 19 return false;
785 }
786 r_name = foreign->referenced_col_names[i];
787 } /* each column in reference element */
788 } /* each element in reference set */
789 } /* each column being renamed */
790 }
791 }
792
793 363 return true;
794 }
795
796 /** Determine if one ALTER TABLE can be done instantly on the table
797 @param[in] ha_alter_info The DDL operation
798 @param[in] table InnoDB table
799 @param[in] old_table old TABLE
800 @param[in] altered_table new TABLE
801 @return Instant_Type accordingly */
802 57575 static inline Instant_Type innobase_support_instant(
803 const Alter_inplace_info *ha_alter_info, const dict_table_t *table,
804 const TABLE *old_table, const TABLE *altered_table) {
805
2/2
✓ Branch 0 taken 6665 times.
✓ Branch 1 taken 50910 times.
57575 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
806 6665 return (Instant_Type::INSTANT_NO_CHANGE);
807 }
808
809 50910 Alter_inplace_info::HA_ALTER_FLAGS alter_inplace_flags =
810 50910 ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE;
811
812
2/2
✓ Branch 0 taken 36982 times.
✓ Branch 1 taken 13928 times.
50910 if (alter_inplace_flags & ~INNOBASE_INSTANT_ALLOWED) {
813 36982 return (Instant_Type::INSTANT_IMPOSSIBLE);
814 }
815
816 /* During upgrade, if columns are added in system tables, avoid instant */
817
2/2
✓ Branch 0 taken 1875 times.
✓ Branch 1 taken 12053 times.
13928 if (current_thd->is_server_upgrade_thread()) {
818 1875 return (Instant_Type::INSTANT_IMPOSSIBLE);
819 }
820
821 enum class INSTANT_OPERATION {
822 COLUMN_RENAME_ONLY, /*!< Only column RENAME */
823 VIRTUAL_ADD_DROP_ONLY, /*!< Only virtual column ADD AND DROP */
824 VIRTUAL_ADD_DROP_WITH_RENAME, /*!< Virtual column ADD/DROP with RENAME */
825 INSTANT_ADD, /*< INSTANT ADD possibly with virtual column ADD and
826 column RENAME */
827 INSTANT_DROP, /*|< INSTANT DROP possibly with virtual column ADD/DROP and
828 column RENAME */
829 NONE
830 };
831
832 12053 enum INSTANT_OPERATION op = INSTANT_OPERATION::NONE;
833
834
2/2
✓ Branch 0 taken 322 times.
✓ Branch 1 taken 11731 times.
12053 if (!(alter_inplace_flags & ~Alter_inplace_info::ALTER_COLUMN_NAME)) {
835 322 op = INSTANT_OPERATION::COLUMN_RENAME_ONLY;
836
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 11596 times.
11731 } else if (!(alter_inplace_flags &
837 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
838 Alter_inplace_info::DROP_VIRTUAL_COLUMN))) {
839 135 op = INSTANT_OPERATION::VIRTUAL_ADD_DROP_ONLY;
840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11596 times.
11596 } else if (!(alter_inplace_flags &
841 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
842 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
843 Alter_inplace_info::ALTER_COLUMN_NAME))) {
844 op = INSTANT_OPERATION::VIRTUAL_ADD_DROP_WITH_RENAME;
845
2/2
✓ Branch 0 taken 8973 times.
✓ Branch 1 taken 2623 times.
11596 } else if (alter_inplace_flags & Alter_inplace_info::ADD_STORED_BASE_COLUMN &&
846
1/2
✓ Branch 0 taken 8973 times.
✗ Branch 1 not taken.
8973 !(alter_inplace_flags & Alter_inplace_info::DROP_VIRTUAL_COLUMN)) {
847 8973 op = INSTANT_OPERATION::INSTANT_ADD;
848
2/2
✓ Branch 0 taken 2574 times.
✓ Branch 1 taken 49 times.
2623 } else if (alter_inplace_flags & Alter_inplace_info::DROP_STORED_COLUMN) {
849 2574 op = INSTANT_OPERATION::INSTANT_DROP;
850 }
851
852
5/7
✓ Branch 0 taken 322 times.
✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2574 times.
✓ Branch 4 taken 8973 times.
✓ Branch 5 taken 49 times.
✗ Branch 6 not taken.
12053 switch (op) {
853 322 case INSTANT_OPERATION::COLUMN_RENAME_ONLY: {
854 322 bool report_error = (ha_alter_info->alter_info->requested_algorithm ==
855 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT);
856
2/2
✓ Branch 0 taken 299 times.
✓ Branch 1 taken 23 times.
322 if (ok_to_rename_column(ha_alter_info, old_table, altered_table, table,
857 true, report_error)) {
858 299 return (Instant_Type::INSTANT_COLUMN_RENAME);
859 }
860 23 } break;
861 135 case INSTANT_OPERATION::VIRTUAL_ADD_DROP_ONLY:
862
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 1 times.
135 if (check_v_col_in_order(old_table, altered_table, ha_alter_info)) {
863 134 return (Instant_Type::INSTANT_VIRTUAL_ONLY);
864 }
865 1 break;
866 case INSTANT_OPERATION::VIRTUAL_ADD_DROP_WITH_RENAME:
867 /* Not supported yet in INPLACE. So not supporting here as well. */
868 break;
869 2574 case INSTANT_OPERATION::INSTANT_DROP:
870 /* Disable INSTANT DROP unless explicitly requested. So ALGORITHM=DEFAULT
871 will use INPLACE/COPY */
872
2/2
✓ Branch 0 taken 1334 times.
✓ Branch 1 taken 1240 times.
2574 if (ha_alter_info->alter_info->requested_algorithm ==
873 Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) {
874 1334 break;
875 }
876
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1240 times.
1240 if (!check_v_col_in_order(old_table, altered_table, ha_alter_info)) {
877 break;
878 }
879 [[fallthrough]];
880 case INSTANT_OPERATION::INSTANT_ADD:
881
882 /* Disable INSTANT DROP unless explicitly requested. So ALGORITHM=DEFAULT
883 will use INPLACE/COPY */
884
2/2
✓ Branch 0 taken 6590 times.
✓ Branch 1 taken 3623 times.
10213 if (ha_alter_info->alter_info->requested_algorithm ==
885 Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) {
886 6590 break;
887 }
888 /* If it's an ADD COLUMN without changing existing stored column orders
889 (change trailing virtual column orders is fine, especially for supporting
890 adding stored columns to a table with functional indexes), or including
891 ADD VIRTUAL COLUMN */
892
2/2
✓ Branch 0 taken 3595 times.
✓ Branch 1 taken 28 times.
3623 if (table->support_instant_add_drop()) {
893 3595 return (Instant_Type::INSTANT_ADD_DROP_COLUMN);
894 }
895 28 break;
896 49 case INSTANT_OPERATION::NONE:
897 49 break;
898 }
899
900 8025 return (Instant_Type::INSTANT_IMPOSSIBLE);
901 }
902
903 /** Determine if this is an instant ALTER TABLE.
904 This can be checked in *inplace_alter_table() functions, which are called
905 after check_if_supported_inplace_alter()
906 @param[in] ha_alter_info The DDL operation
907 @return whether it's an instant ALTER TABLE */
908 425816 static inline bool is_instant(const Alter_inplace_info *ha_alter_info) {
909 425816 return (ha_alter_info->handler_trivial_ctx !=
910 425816 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE));
911 }
912
913 /** Determine if ALTER TABLE needs to rebuild the table.
914 @param[in] ha_alter_info The DDL operation
915 @param[in] old_table the table we are changing
916 @param[in] is_file_per_table true if table is file_per_table
917 @return whether it is necessary to rebuild the table */
918 198007 [[nodiscard]] static bool innobase_need_rebuild(
919 const Alter_inplace_info *ha_alter_info, const TABLE *old_table,
920 bool is_file_per_table) {
921
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 197999 times.
198007 if (is_instant(ha_alter_info)) {
922 8 return (false);
923 }
924
925 197999 Alter_inplace_info::HA_ALTER_FLAGS alter_inplace_flags =
926 197999 ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE);
927
928 const bool was_none_explicitly_specified{
929 197999 Encryption::none_explicitly_specified(old_table->s->explicit_encryption,
930 197999 old_table->s->encrypt_type.str)};
931
932 197999 const bool is_none_explicitly_specified{Encryption::none_explicitly_specified(
933 197999 ha_alter_info->create_info->explicit_encryption,
934 197999 ha_alter_info->create_info->encrypt_type.str)};
935
936
4/4
✓ Branch 0 taken 11230 times.
✓ Branch 1 taken 120197 times.
✓ Branch 2 taken 2440 times.
✓ Branch 3 taken 8790 times.
131427 if ((!was_none_explicitly_specified && is_none_explicitly_specified &&
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 189209 times.
189209 is_file_per_table) ||
938 189209 (Encryption::is_keyring(ha_alter_info->create_info->encrypt_type.str) &&
939
4/6
✓ Branch 0 taken 131427 times.
✓ Branch 1 taken 66572 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8790 times.
✓ Branch 5 taken 189209 times.
395998 !Encryption::is_keyring(old_table->s->encrypt_type.str)) ||
940 189209 ha_alter_info->create_info->encryption_key_id !=
941
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 189209 times.
189209 old_table->s->encryption_key_id)
942 8790 return true;
943
944
2/2
✓ Branch 0 taken 92607 times.
✓ Branch 1 taken 96602 times.
189209 if (alter_inplace_flags == Alter_inplace_info::CHANGE_CREATE_OPTION &&
945
2/2
✓ Branch 0 taken 33283 times.
✓ Branch 1 taken 59324 times.
92607 !(ha_alter_info->create_info->used_fields &
946 (HA_CREATE_USED_ROW_FORMAT | HA_CREATE_USED_KEY_BLOCK_SIZE |
947 HA_CREATE_USED_TABLESPACE))) {
948 /* Any other CHANGE_CREATE_OPTION than changing
949 ROW_FORMAT, KEY_BLOCK_SIZE or TABLESPACE can be done
950 without rebuilding the table. */
951 33283 return (false);
952 }
953
954 155926 return (!!(ha_alter_info->handler_flags & INNOBASE_ALTER_REBUILD));
955 }
956
957 /** Check if InnoDB supports a particular alter table in-place
958 @param altered_table TABLE object for new version of table.
959 @param ha_alter_info Structure describing changes to be done
960 by ALTER TABLE and holding data used during in-place alter.
961
962 @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported
963 @retval HA_ALTER_INPLACE_NO_LOCK Supported
964 @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE Supported, but requires
965 lock during main phase and exclusive lock during prepare phase.
966 @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE Supported, prepare phase
967 requires exclusive lock (any transactions that have accessed the table
968 must commit or roll back first, and no transactions can access the table
969 while prepare_inplace_alter_table() is executing)
970 */
971 57699 enum_alter_inplace_result ha_innobase::check_if_supported_inplace_alter(
972 TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
973
1/2
✓ Branch 0 taken 57699 times.
✗ Branch 1 not taken.
57699 DBUG_TRACE;
974
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57699 times.
57699 if (srv_sys_space.created_new_raw()) {
976 return HA_ALTER_INPLACE_NOT_SUPPORTED;
977 }
978
979
3/4
✓ Branch 0 taken 57687 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57687 times.
57699 if (high_level_read_only || srv_force_recovery) {
980
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (srv_force_recovery) {
981
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 my_error(ER_INNODB_FORCED_RECOVERY, MYF(0));
982 } else {
983
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 my_error(ER_READ_ONLY_MODE, MYF(0));
984 }
985 12 return HA_ALTER_ERROR;
986 }
987
988
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 57679 times.
57687 if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
989 /* Deny the inplace ALTER TABLE. MySQL will try to
990 re-create the table and ha_innobase::create() will
991 return an error too. This is how we effectively
992 deny adding too many columns to a table. */
993 8 ha_alter_info->unsupported_reason =
994
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 innobase_get_err_msg(ER_TOO_MANY_FIELDS);
995 8 return HA_ALTER_INPLACE_NOT_SUPPORTED;
996 }
997
998 /* We don't support change Master key encryption attribute with
999 inplace algorithm. */
1000 57679 char *old_encryption = this->table->s->encrypt_type.str;
1001 57679 char *new_encryption = altered_table->s->encrypt_type.str;
1002
1003
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 57321 times.
115358 if (Encryption::is_master_key_encryption(old_encryption) !=
1004 57679 Encryption::is_master_key_encryption(new_encryption)) {
1005 358 ha_alter_info->unsupported_reason =
1006
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_ENCRYPTION_INPLACE);
1007 358 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1008 }
1009
1010
1/2
✓ Branch 0 taken 57321 times.
✗ Branch 1 not taken.
57321 update_thd();
1011
1012
2/2
✓ Branch 0 taken 3586 times.
✓ Branch 1 taken 53735 times.
57321 if (ha_alter_info->handler_flags &
1013 ~(INNOBASE_INPLACE_IGNORE | INNOBASE_ALTER_NOREBUILD |
1014 INNOBASE_ALTER_REBUILD)) {
1015
2/2
✓ Branch 0 taken 3450 times.
✓ Branch 1 taken 136 times.
3586 if (ha_alter_info->handler_flags &
1016 Alter_inplace_info::ALTER_STORED_COLUMN_TYPE) {
1017
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3440 times.
3450 if (ha_alter_info->alter_info->requested_algorithm ==
1018 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1019
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1020 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE_INSTANT);
1021 } else {
1022
1/2
✓ Branch 0 taken 3440 times.
✗ Branch 1 not taken.
3440 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1023 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE);
1024 }
1025 }
1026 3586 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1027 }
1028
1029 /* Only support online add foreign key constraint when check_foreigns is
1030 turned off */
1031
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 53565 times.
53735 if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_FOREIGN_KEY) &&
1032
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 96 times.
170 m_prebuilt->trx->check_foreigns) {
1033 74 ha_alter_info->unsupported_reason =
1034
1/2
✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
74 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK);
1035 74 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1036 }
1037
1038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53661 times.
53661 if (altered_table->file->ht != ht) {
1039 /* Non-native partitioning table engine. No longer supported, due to
1040 implementation of native InnoDB partitioning. */
1041 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1042 }
1043
1044 107322 Instant_Type instant_type = innobase_support_instant(
1045
1/2
✓ Branch 0 taken 53661 times.
✗ Branch 1 not taken.
53661 ha_alter_info, m_prebuilt->table, this->table, altered_table);
1046
1047 53661 ha_alter_info->handler_trivial_ctx =
1048 53661 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE);
1049
1050
3/4
✓ Branch 0 taken 53661 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52860 times.
✓ Branch 3 taken 801 times.
53661 if (!dict_table_is_partition(m_prebuilt->table)) {
1051
3/4
✓ Branch 0 taken 45243 times.
✓ Branch 1 taken 747 times.
✓ Branch 2 taken 6870 times.
✗ Branch 3 not taken.
52860 switch (instant_type) {
1052 45243 case Instant_Type::INSTANT_IMPOSSIBLE:
1053 45243 break;
1054 747 case Instant_Type::INSTANT_ADD_DROP_COLUMN:
1055
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 679 times.
747 if (ha_alter_info->alter_info->requested_algorithm ==
1056 Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) {
1057 /* Still fall back to INPLACE since the behaviour is different */
1058 68 break;
1059
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 679 times.
679 } else if (m_prebuilt->table->n_def == REC_MAX_N_FIELDS) {
1060 if (ha_alter_info->alter_info->requested_algorithm ==
1061 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1062 my_error(ER_TOO_MANY_FIELDS, MYF(0),
1063 m_prebuilt->table->name.m_name);
1064 return HA_ALTER_ERROR;
1065 }
1066 /* INSTANT can't be done any more. Fall back to INPLACE. */
1067 break;
1068
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 677 times.
679 } else if (m_prebuilt->table->current_row_version == MAX_ROW_VERSION) {
1069
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ha_alter_info->alter_info->requested_algorithm ==
1070 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1071 2 my_error(ER_INNODB_MAX_ROW_VERSION, MYF(0),
1072
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_prebuilt->table->name.m_name);
1073 2 return HA_ALTER_ERROR;
1074 }
1075
1076 /* INSTANT can't be done any more. Fall back to INPLACE. */
1077 break;
1078
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 673 times.
677 } else if (!Instant_ddl_impl<dd::Table>::is_instant_add_possible(
1079 677 ha_alter_info, table, altered_table,
1080
1/2
✓ Branch 0 taken 677 times.
✗ Branch 1 not taken.
677 m_prebuilt->table)) {
1081
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (ha_alter_info->alter_info->requested_algorithm ==
1082 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1083 /* Try to find if after adding columns, any possible row stays
1084 within permissible limit. If it doesn't, return error. */
1085
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 my_error(ER_INNODB_INSTANT_ADD_NOT_SUPPORTED_MAX_SIZE, MYF(0));
1086 4 return HA_ALTER_ERROR;
1087 }
1088
1089 /* INSTANT can't be done. Fall back to INPLACE. */
1090 break;
1091
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 665 times.
673 } else if (ha_alter_info->error_if_not_empty) {
1092 /* In this case, it can't be instant because the table
1093 may not be empty. Have to fall back to INPLACE */
1094 8 break;
1095 }
1096 [[fallthrough]];
1097 case Instant_Type::INSTANT_NO_CHANGE:
1098 case Instant_Type::INSTANT_VIRTUAL_ONLY:
1099 case Instant_Type::INSTANT_COLUMN_RENAME:
1100 7535 ha_alter_info->handler_trivial_ctx = instant_type_to_int(instant_type);
1101 7535 return HA_ALTER_INPLACE_INSTANT;
1102 }
1103 }
1104
1105 /* Only support NULL -> NOT NULL change if strict table sql_mode
1106 is set. Fall back to COPY for conversion if not strict tables.
1107 In-Place will fail with an error when trying to convert
1108 NULL to a NOT NULL value. */
1109 92240 if ((ha_alter_info->handler_flags &
1110
4/4
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 45872 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 46047 times.
46368 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) &&
1111
3/4
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 175 times.
248 !thd_is_strict_mode(m_user_thd)) {
1112 73 ha_alter_info->unsupported_reason =
1113
1/2
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
73 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
1114 73 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1115 }
1116
1117 /* DROP PRIMARY KEY is only allowed in combination with ADD
1118 PRIMARY KEY. */
1119
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 45926 times.
46047 if ((ha_alter_info->handler_flags & (Alter_inplace_info::ADD_PK_INDEX |
1120 Alter_inplace_info::DROP_PK_INDEX)) ==
1121 Alter_inplace_info::DROP_PK_INDEX) {
1122 121 ha_alter_info->unsupported_reason =
1123
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK);
1124 121 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1125 }
1126
1127 /* If a column change from NOT NULL to NULL,
1128 and there's a implict pk on this column. the
1129 table should be rebuild. The change should
1130 only go through the "Copy" method. */
1131
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 45730 times.
45926 if ((ha_alter_info->handler_flags &
1132 Alter_inplace_info::ALTER_COLUMN_NULLABLE)) {
1133 196 const uint my_primary_key = altered_table->s->primary_key;
1134
1135 /* See if MYSQL table has no pk but we do. */
1136
3/4
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 196 times.
269 if (UNIV_UNLIKELY(my_primary_key >= MAX_KEY) &&
1137
2/4
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 73 times.
73 !row_table_got_default_clust_index(m_prebuilt->table)) {
1138 ha_alter_info->unsupported_reason =
1139 innobase_get_err_msg(ER_PRIMARY_CANT_HAVE_NULL);
1140 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1141 }
1142 }
1143
1144 45926 bool add_drop_v_cols = false;
1145
1146 /* If there is add or drop virtual columns, we will support operations
1147 with these 3 options alone with inplace interface for now */
1148
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 45136 times.
45926 if (ha_alter_info->handler_flags &
1149 (Alter_inplace_info::ADD_VIRTUAL_COLUMN |
1150 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
1151 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER)) {
1152 790 ulonglong flags = ha_alter_info->handler_flags;
1153
1154 /* TODO: uncomment the flags below, once we start to
1155 support them */
1156 790 flags &=
1157 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
1158 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
1159 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER
1160 /*
1161 | Alter_inplace_info::ALTER_STORED_COLUMN_ORDER
1162 | Alter_inplace_info::ADD_STORED_BASE_COLUMN
1163 | Alter_inplace_info::DROP_STORED_COLUMN
1164 | Alter_inplace_info::ALTER_STORED_COLUMN_ORDER
1165 | Alter_inplace_info::ADD_UNIQUE_INDEX
1166 */
1167 | Alter_inplace_info::ADD_INDEX | Alter_inplace_info::DROP_INDEX);
1168
1169 1209 if (flags != 0 ||
1170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 419 times.
419 (altered_table->s->partition_info_str &&
1171
4/6
✓ Branch 0 taken 419 times.
✓ Branch 1 taken 371 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 378 times.
✓ Branch 5 taken 412 times.
1209 altered_table->s->partition_info_str_len) ||
1172
3/4
✓ Branch 0 taken 419 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 412 times.
419 (!check_v_col_in_order(this->table, altered_table, ha_alter_info))) {
1173 378 ha_alter_info->unsupported_reason =
1174
1/2
✓ Branch 0 taken 378 times.
✗ Branch 1 not taken.
378 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1175 378 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1176 }
1177
1178 412 add_drop_v_cols = true;
1179 }
1180
1181 /* We should be able to do the operation in-place.
1182 See if we can do it online (LOCK=NONE). */
1183 45548 bool online = true;
1184
1185 List_iterator_fast<Create_field> cf_it(
1186
1/2
✓ Branch 0 taken 45548 times.
✗ Branch 1 not taken.
45548 ha_alter_info->alter_info->create_list);
1187
1188 /* Fix the key parts. */
1189 93812 for (KEY *new_key = ha_alter_info->key_info_buffer;
1190
2/2
✓ Branch 0 taken 48265 times.
✓ Branch 1 taken 45547 times.
93812 new_key < ha_alter_info->key_info_buffer + ha_alter_info->key_count;
1191 new_key++) {
1192 /* Do not support adding/dropping a virtual column, while
1193 there is a table rebuild caused by adding a new FTS_DOC_ID */
1194
4/6
✓ Branch 0 taken 1524 times.
✓ Branch 1 taken 46741 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1524 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 48265 times.
48265 if ((new_key->flags & HA_FULLTEXT) && add_drop_v_cols &&
1195 !DICT_TF2_FLAG_IS_SET(m_prebuilt->table, DICT_TF2_FTS_HAS_DOC_ID)) {
1196 ha_alter_info->unsupported_reason =
1197 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1198 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1199 }
1200
1201 122496 for (KEY_PART_INFO *key_part = new_key->key_part;
1202
2/2
✓ Branch 0 taken 74232 times.
✓ Branch 1 taken 48264 times.
122496 key_part < new_key->key_part + new_key->user_defined_key_parts;
1203 key_part++) {
1204 74232 const Create_field *new_field = nullptr;
1205
1206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74232 times.
74232 assert(key_part->fieldnr < altered_table->s->fields);
1207
1208 74232 cf_it.rewind();
1209
2/2
✓ Branch 0 taken 222345 times.
✓ Branch 1 taken 74232 times.
296577 for (auto fieldnr = 0; fieldnr != key_part->fieldnr + 1; fieldnr++) {
1210 222345 new_field = cf_it++;
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 222345 times.
222345 assert(new_field);
1212 }
1213
1214 74232 key_part->field = altered_table->field[key_part->fieldnr];
1215 /* In some special cases InnoDB emits "false"
1216 duplicate key errors with NULL key values. Let
1217 us play safe and ensure that we can correctly
1218 print key values even in such cases. */
1219
1/2
✓ Branch 0 taken 74232 times.
✗ Branch 1 not taken.
74232 key_part->null_offset = key_part->field->null_offset();
1220 74232 key_part->null_bit = key_part->field->null_bit;
1221
1222
2/2
✓ Branch 0 taken 73919 times.
✓ Branch 1 taken 313 times.
74232 if (new_field->field) {
1223 /* This is an existing column. */
1224 73919 continue;
1225 }
1226
1227 /* This is an added column. */
1228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
313 assert(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
1229
1230 /* We cannot replace a hidden FTS_DOC_ID
1231 with a user-visible FTS_DOC_ID. */
1232
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 312 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 313 times.
314 if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table) &&
1233
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 !my_strcasecmp(system_charset_info, key_part->field->field_name,
1234 FTS_DOC_ID_COL_NAME)) {
1235 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1236 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS);
1237 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1238 }
1239
1240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
313 assert(((key_part->field->auto_flags & Field::NEXT_NUMBER) != 0) ==
1241 key_part->field->is_flag_set(AUTO_INCREMENT_FLAG));
1242
1243
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 270 times.
313 if (key_part->field->is_flag_set(AUTO_INCREMENT_FLAG)) {
1244 /* We cannot assign an AUTO_INCREMENT
1245 column values during online ALTER. */
1246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 assert(key_part->field == altered_table->found_next_number_field);
1247
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1248 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC);
1249 43 online = false;
1250 }
1251
1252
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 130 times.
313 if (key_part->field->is_virtual_gcol()) {
1253 /* Do not support adding index on newly added
1254 virtual column, while there is also a drop
1255 virtual column in the same clause */
1256
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 182 times.
183 if (ha_alter_info->handler_flags &
1257 Alter_inplace_info::DROP_VIRTUAL_COLUMN) {
1258
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1259 ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1260
1261 1 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1262 }
1263
1264 182 ha_alter_info->unsupported_reason =
1265
1/2
✓ Branch 0 taken 182 times.
✗ Branch 1 not taken.
182 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN);
1266 182 online = false;
1267 }
1268 }
1269 }
1270
1271
3/4
✓ Branch 0 taken 535 times.
✓ Branch 1 taken 45012 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 535 times.
45547 assert(!m_prebuilt->table->fts ||
1272 m_prebuilt->table->fts->doc_col <= table->s->fields);
1273
4/6
✓ Branch 0 taken 535 times.
✓ Branch 1 taken 45012 times.
✓ Branch 2 taken 535 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 535 times.
45547 assert(!m_prebuilt->table->fts || m_prebuilt->table->fts->doc_col <
1274 m_prebuilt->table->get_n_user_cols());
1275
1276
2/2
✓ Branch 0 taken 83 times.
✓ Branch 1 taken 45464 times.
45547 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_SPATIAL_INDEX) {
1277 83 ha_alter_info->unsupported_reason =
1278
1/2
✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
83 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1279 83 online = false;
1280 }
1281
1282
6/6
✓ Branch 0 taken 535 times.
✓ Branch 1 taken 45012 times.
✓ Branch 2 taken 397 times.
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 397 times.
✓ Branch 5 taken 45150 times.
45547 if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table)) {
1283 /* FULLTEXT indexes are supposed to remain. */
1284 /* Disallow DROP INDEX FTS_DOC_ID_INDEX */
1285
1286
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 397 times.
418 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
1287
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
21 if (!my_strcasecmp(system_charset_info,
1288 ha_alter_info->index_drop_buffer[i]->name,
1289 FTS_DOC_ID_INDEX_NAME)) {
1290 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1291 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1292 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1293 }
1294 }
1295
1296 /* InnoDB can have a hidden FTS_DOC_ID_INDEX on a
1297 visible FTS_DOC_ID column as well. Prevent dropping or
1298 renaming the FTS_DOC_ID. */
1299
1300
2/2
✓ Branch 0 taken 2558 times.
✓ Branch 1 taken 392 times.
2950 for (Field **fp = table->field; *fp; fp++) {
1301
4/4
✓ Branch 0 taken 2552 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2547 times.
✓ Branch 3 taken 11 times.
5110 if (!((*fp)->is_flag_set(FIELD_IS_RENAMED) ||
1302
2/2
✓ Branch 0 taken 2547 times.
✓ Branch 1 taken 5 times.
2552 (*fp)->is_flag_set(FIELD_IS_DROPPED))) {
1303 2547 continue;
1304 }
1305
1306
3/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 6 times.
11 if (!my_strcasecmp(system_charset_info, (*fp)->field_name,
1307 FTS_DOC_ID_COL_NAME)) {
1308
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1309 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1310 5 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1311 }
1312 }
1313 }
1314
1315 45542 m_prebuilt->trx->will_lock++;
1316
1317
2/2
✓ Branch 0 taken 45250 times.
✓ Branch 1 taken 292 times.
45542 if (!online) {
1318 /* We already determined that only a non-locking
1319 operation is possible. */
1320 90500 } else if (((ha_alter_info->handler_flags &
1321
2/2
✓ Branch 0 taken 25096 times.
✓ Branch 1 taken 18564 times.
43660 Alter_inplace_info::ADD_PK_INDEX) ||
1322 43660 innobase_need_rebuild(
1323 ha_alter_info, altered_table,
1324
7/8
✓ Branch 0 taken 43660 times.
✓ Branch 1 taken 1590 times.
✓ Branch 2 taken 43660 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26656 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 49 times.
✓ Branch 7 taken 45201 times.
117156 dict_table_is_file_per_table(m_prebuilt->table))) &&
1325
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 26637 times.
53342 (innobase_fulltext_exist(altered_table) ||
1326 26656 innobase_spatial_exist(altered_table))) {
1327 /* Refuse to rebuild the table online, if
1328 FULLTEXT OR SPATIAL indexes are to survive the rebuild. */
1329 49 online = false;
1330 /* If the table already contains fulltext indexes,
1331 refuse to rebuild the table natively altogether. */
1332
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 23 times.
49 if (m_prebuilt->table->fts) {
1333 26 ha_alter_info->unsupported_reason =
1334
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 innobase_get_err_msg(ER_INNODB_FT_LIMIT);
1335 26 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1336 }
1337
1338
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 if (innobase_spatial_exist(altered_table)) {
1339 19 ha_alter_info->unsupported_reason =
1340
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1341 } else {
1342 4 ha_alter_info->unsupported_reason =
1343
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1344 }
1345
2/2
✓ Branch 0 taken 4932 times.
✓ Branch 1 taken 40269 times.
45201 } else if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX)) {
1346 /* Building a full-text index requires a lock.
1347 We could do without a lock if the table already contains
1348 an FTS_DOC_ID column, but in that case we would have
1349 to apply the modification log to the full-text indexes. */
1350
1351
2/2
✓ Branch 0 taken 5171 times.
✓ Branch 1 taken 4464 times.
9635 for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
1352 5171 const KEY *key =
1353 5171 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
1354
2/2
✓ Branch 0 taken 468 times.
✓ Branch 1 taken 4703 times.
5171 if (key->flags & HA_FULLTEXT) {
1355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 468 times.
468 assert(!(key->flags & HA_KEYFLAG_MASK &
1356 ~(HA_FULLTEXT | HA_PACK_KEY | HA_GENERATED_KEY |
1357 HA_BINARY_PACK_KEY)));
1358 468 ha_alter_info->unsupported_reason =
1359
1/2
✓ Branch 0 taken 468 times.
✗ Branch 1 not taken.
468 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1360 468 online = false;
1361 468 break;
1362 }
1363 }
1364 }
1365
1366
2/2
✓ Branch 0 taken 44733 times.
✓ Branch 1 taken 783 times.
45516 return online ? HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
1367 45516 : HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE;
1368 57699 }
1369
1370 /** Update the metadata in prepare phase. This only check if dd::Tablespace
1371 should be removed or(and) created, because to remove and store dd::Tablespace
1372 could fail, so it's better to do it earlier, to prevent a late rollback
1373 @param[in,out] thd MySQL connection
1374 @param[in] old_table Old InnoDB table object
1375 @param[in,out] new_table New InnoDB table object
1376 @param[in] old_dd_tab Old dd::Table or dd::Partition
1377 @return false On success
1378 @retval true On failure */
1379 template <typename Table>
1380 [[nodiscard]] static bool dd_prepare_inplace_alter_table(
1381 THD *thd, const dict_table_t *old_table, dict_table_t *new_table,
1382 const Table *old_dd_tab);
1383
1384 /** Update metadata in commit phase. Note this function should only update
1385 the metadata which would not result in failure
1386 @param[in] old_info Some table information for the old table
1387 @param[in,out] new_table New InnoDB table object
1388 @param[in] old_dd_tab Old dd::Table or dd::Partition
1389 @param[in,out] new_dd_tab New dd::Table or dd::Partition */
1390 template <typename Table>
1391 static void dd_commit_inplace_alter_table(
1392 const alter_table_old_info_t &old_info, dict_table_t *new_table,
1393 const Table *old_dd_tab, Table *new_dd_tab);
1394
1395 /** Update metadata in commit phase when the alter table does
1396 no change to the table
1397 @param[in] ha_alter_info the DDL operation
1398 @param[in] old_dd_tab Old dd::Table or dd::Partition
1399 @param[in] new_dd_tab New dd::Table or dd::Partition
1400 @param[in] ignore_fts ignore FTS update if true */
1401 template <typename Table>
1402 static void dd_commit_inplace_no_change(const Alter_inplace_info *ha_alter_info,
1403 const Table *old_dd_tab,
1404 Table *new_dd_tab, bool ignore_fts);
1405
1406 /** Update table level instant metadata in commit phase
1407 @param[in] table InnoDB table object
1408 @param[in] old_dd_tab old dd::Table
1409 @param[in] new_dd_tab new dd::Table */
1410 static void dd_commit_inplace_update_instant_meta(const dict_table_t *table,
1411 const dd::Table *old_dd_tab,
1412 dd::Table *new_dd_tab);
1413
1414 /** Allows InnoDB to update internal structures with concurrent
1415 writes blocked (provided that check_if_supported_inplace_alter()
1416 did not return HA_ALTER_INPLACE_NO_LOCK).
1417 This will be invoked before inplace_alter_table().
1418 @param[in] altered_table TABLE object for new version of table.
1419 @param[in,out] ha_alter_info Structure describing changes to be done
1420 by ALTER TABLE and holding data used during in-place alter.
1421 @param[in] old_dd_tab dd::Table object describing old version
1422 of the table.
1423 @param[in,out] new_dd_tab dd::Table object for the new version of
1424 the table. Can be adjusted by this call. Changes to the table definition will
1425 be persisted in the data-dictionary at statement commit time.
1426 @retval true Failure
1427 @retval false Success
1428 */
1429 52196 bool ha_innobase::prepare_inplace_alter_table(TABLE *altered_table,
1430 Alter_inplace_info *ha_alter_info,
1431 const dd::Table *old_dd_tab,
1432 dd::Table *new_dd_tab) {
1433
1/2
✓ Branch 0 taken 52196 times.
✗ Branch 1 not taken.
52196 DBUG_TRACE;
1434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52196 times.
52196 ut_ad(old_dd_tab != nullptr);
1435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52196 times.
52196 ut_ad(new_dd_tab != nullptr);
1436
1437
7/8
✓ Branch 0 taken 52196 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 52189 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 52192 times.
52203 if (dict_sys_t::is_dd_table_id(m_prebuilt->table->id) &&
1438 7 innobase_need_rebuild(ha_alter_info, table,
1439
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 dict_table_is_file_per_table(m_prebuilt->table))) {
1440
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 ut_ad(!m_prebuilt->table->is_temporary());
1441
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 my_error(ER_NOT_ALLOWED_COMMAND, MYF(0));
1442 4 return true;
1443 }
1444
1445
2/2
✓ Branch 0 taken 4241 times.
✓ Branch 1 taken 47951 times.
52192 if (altered_table->found_next_number_field != nullptr) {
1446
2/4
✓ Branch 0 taken 4241 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4241 times.
✗ Branch 3 not taken.
4241 dd_copy_autoinc(old_dd_tab->se_private_data(),
1447
1/2
✓ Branch 0 taken 4241 times.
✗ Branch 1 not taken.
4241 new_dd_tab->se_private_data());
1448
1/2
✓ Branch 0 taken 4241 times.
✗ Branch 1 not taken.
4241 dd_set_autoinc(new_dd_tab->se_private_data(),
1449
1/2
✓ Branch 0 taken 4241 times.
✗ Branch 1 not taken.
4241 ha_alter_info->create_info->auto_increment_value);
1450 }
1451
1452
1/2
✓ Branch 0 taken 52143 times.
✗ Branch 1 not taken.
52192 return prepare_inplace_alter_table_impl<dd::Table>(
1453 52143 altered_table, ha_alter_info, old_dd_tab, new_dd_tab);
1454 52147 }
1455
1456 int ha_innobase::parallel_scan_init(void *&scan_ctx, size_t *num_threads,
1457 bool use_reserved_threads) {
1458 if (dict_table_is_discarded(m_prebuilt->table)) {
1459 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
1460 m_prebuilt->table->name.m_name);
1461
1462 return (HA_ERR_NO_SUCH_TABLE);
1463 }
1464
1465 scan_ctx = nullptr;
1466
1467 update_thd();
1468
1469 auto trx = m_prebuilt->trx;
1470
1471 innobase_register_trx(ht, ha_thd(), trx);
1472
1473 trx_start_if_not_started_xa(trx, false, UT_LOCATION_HERE);
1474
1475 trx_assign_read_view(trx);
1476
1477 size_t max_threads = thd_parallel_read_threads(m_prebuilt->trx->mysql_thd);
1478
1479 max_threads =
1480 Parallel_reader::available_threads(max_threads, use_reserved_threads);
1481
1482 if (max_threads == 0) {
1483 return (HA_ERR_GENERIC);
1484 }
1485
1486 const auto row_len = m_prebuilt->mysql_row_len;
1487
1488 auto adapter = ut::new_withkey<Parallel_reader_adapter>(
1489 UT_NEW_THIS_FILE_PSI_KEY, max_threads, row_len);
1490
1491 if (adapter == nullptr) {
1492 Parallel_reader::release_threads(max_threads);
1493 return (HA_ERR_OUT_OF_MEM);
1494 }
1495
1496 Parallel_reader::Scan_range full_scan{};
1497
1498 Parallel_reader::Config config(full_scan, m_prebuilt->table->first_index());
1499
1500 dberr_t err =
1501 adapter->add_scan(trx, config, [=](const Parallel_reader::Ctx *ctx) {
1502 return (adapter->process_rows(ctx));
1503 });
1504
1505 if (err != DB_SUCCESS) {
1506 ut::delete_(adapter);
1507 return (convert_error_code_to_mysql(err, 0, ha_thd()));
1508 }
1509
1510 scan_ctx = adapter;
1511 *num_threads = max_threads;
1512
1513 build_template(true);
1514
1515 adapter->set(m_prebuilt);
1516
1517 return (0);
1518 }
1519
1520 int ha_innobase::parallel_scan(void *scan_ctx, void **thread_ctxs,
1521 Reader::Init_fn init_fn, Reader::Load_fn load_fn,
1522 Reader::End_fn end_fn) {
1523 if (dict_table_is_discarded(m_prebuilt->table)) {
1524 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
1525 m_prebuilt->table->name.m_name);
1526
1527 return (HA_ERR_NO_SUCH_TABLE);
1528 }
1529
1530 ut_a(scan_ctx != nullptr);
1531
1532 update_thd();
1533
1534 build_template(true);
1535
1536 auto adapter = static_cast<Parallel_reader_adapter *>(scan_ctx);
1537
1538 auto err = adapter->run(thread_ctxs, init_fn, load_fn, end_fn);
1539
1540 return (convert_error_code_to_mysql(err, 0, ha_thd()));
1541 }
1542
1543 void ha_innobase::parallel_scan_end(void *parallel_scan_ctx) {
1544 Parallel_reader_adapter *parallel_reader =
1545 static_cast<Parallel_reader_adapter *>(parallel_scan_ctx);
1546 ut::delete_(parallel_reader);
1547 }
1548
1549 51899 bool ha_innobase::inplace_alter_table(TABLE *altered_table,
1550 Alter_inplace_info *ha_alter_info,
1551 const dd::Table *old_dd_tab
1552 [[maybe_unused]],
1553 dd::Table *new_dd_tab [[maybe_unused]]) {
1554
1/2
✓ Branch 0 taken 51899 times.
✗ Branch 1 not taken.
51899 DBUG_TRACE;
1555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51899 times.
51899 ut_ad(old_dd_tab != nullptr);
1556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51899 times.
51899 ut_ad(new_dd_tab != nullptr);
1557
1558 /* Notify clone during in place operations */
1559 Clone_notify notifier(Clone_notify::Type::SPACE_ALTER_INPLACE,
1560
1/2
✓ Branch 0 taken 51899 times.
✗ Branch 1 not taken.
51899 dict_sys_t::s_invalid_space_id, false);
1561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51899 times.
51899 ut_ad(!notifier.failed());
1562
1563
1/2
✓ Branch 0 taken 51899 times.
✗ Branch 1 not taken.
103798 return inplace_alter_table_impl<dd::Table>(altered_table, ha_alter_info);
1564 51899 }
1565
1566 /** Commit or rollback the changes made during
1567 prepare_inplace_alter_table() and inplace_alter_table() inside
1568 the storage engine. Note that the allowed level of concurrency
1569 during this operation will be the same as for
1570 inplace_alter_table() and thus might be higher than during
1571 prepare_inplace_alter_table(). (E.g concurrent writes were
1572 blocked during prepare, but might not be during commit).
1573 @param[in] altered_table TABLE object for new version of table.
1574 @param[in,out] ha_alter_info Structure describing changes to be done
1575 by ALTER TABLE and holding data used during in-place alter.
1576 @param[in] commit True to commit or false to rollback.
1577 @param[in] old_dd_tab dd::Table object representing old
1578 version of the table
1579 @param[in,out] new_dd_tab dd::Table object representing new
1580 version of the table. Can be adjusted by this call. Changes to the table
1581 definition will be persisted in the data-dictionary at statement
1582 commit time.
1583 @retval true Failure
1584 @retval false Success */
1585 52157 bool ha_innobase::commit_inplace_alter_table(TABLE *altered_table,
1586 Alter_inplace_info *ha_alter_info,
1587 bool commit,
1588 const dd::Table *old_dd_tab,
1589 dd::Table *new_dd_tab) {
1590
1/2
✓ Branch 0 taken 52157 times.
✗ Branch 1 not taken.
52157 DBUG_TRACE;
1591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52157 times.
52157 ut_ad(old_dd_tab != nullptr);
1592
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52157 times.
52157 ut_ad(new_dd_tab != nullptr);
1593
1594 52157 ha_innobase_inplace_ctx *ctx =
1595 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
1596
1597 52157 alter_table_old_info_t old_info;
1598 52157 ut_d(bool old_info_updated = false);
1599
4/4
✓ Branch 0 taken 51776 times.
✓ Branch 1 taken 381 times.
✓ Branch 2 taken 44247 times.
✓ Branch 3 taken 7529 times.
52157 if (commit && ctx != nullptr) {
1600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44247 times.
44247 ut_ad(!!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE));
1601 44247 old_info.update(ctx->old_table, ctx->need_rebuild());
1602 44247 ut_d(old_info_updated = true);
1603 }
1604
1605
1/2
✓ Branch 0 taken 52119 times.
✗ Branch 1 not taken.
52157 bool res = commit_inplace_alter_table_impl<dd::Table>(
1606 altered_table, ha_alter_info, commit, new_dd_tab);
1607
1608
4/4
✓ Branch 0 taken 52108 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 381 times.
✓ Branch 3 taken 51727 times.
52119 if (res || !commit) {
1609 392 return true;
1610 }
1611
1612
6/8
✓ Branch 0 taken 44198 times.
✓ Branch 1 taken 7529 times.
✓ Branch 2 taken 27636 times.
✓ Branch 3 taken 16562 times.
✓ Branch 4 taken 27636 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 51727 times.
51727 ut_ad(ctx == nullptr || !(ctx->need_rebuild() && is_instant(ha_alter_info)));
1613
1614
2/2
✓ Branch 0 taken 7529 times.
✓ Branch 1 taken 44198 times.
51727 if (is_instant(ha_alter_info)) {
1615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7529 times.
7529 ut_ad(!res);
1616
1617 Instant_ddl_impl<dd::Table> executor(
1618 7529 ha_alter_info, m_user_thd, m_prebuilt->trx, m_prebuilt->table, table,
1619 altered_table, old_dd_tab, new_dd_tab,
1620 7529 altered_table->found_next_number_field != nullptr
1621 272 ? &m_prebuilt->table->autoinc
1622
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 7257 times.
7529 : nullptr);
1623
1624 /* Execute Instant DDL */
1625
1/2
✓ Branch 0 taken 7529 times.
✗ Branch 1 not taken.
7529 executor.commit_instant_ddl();
1626
2/4
✓ Branch 0 taken 44198 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44198 times.
51727 } else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
1627 ctx == nullptr) {
1628 ut_ad(!res);
1629 dd_commit_inplace_no_change(ha_alter_info, old_dd_tab, new_dd_tab, false);
1630 } else {
1631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44198 times.
44198 ut_ad(old_info_updated);
1632
7/8
✓ Branch 0 taken 16562 times.
✓ Branch 1 taken 27636 times.
✓ Branch 2 taken 16562 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16180 times.
✓ Branch 5 taken 382 times.
✓ Branch 6 taken 16180 times.
✓ Branch 7 taken 28018 times.
44198 if (!ctx->need_rebuild() && !dict_table_has_fts_index(m_prebuilt->table)) {
1633 /* Table is not rebuilt so copy instant metadata. */
1634
1/2
✓ Branch 0 taken 16180 times.
✗ Branch 1 not taken.
16180 dd_inplace_alter_copy_instant_metadata(ha_alter_info, old_dd_tab,
1635 new_dd_tab);
1636 }
1637
1638
1/2
✓ Branch 0 taken 44198 times.
✗ Branch 1 not taken.
44198 dd_commit_inplace_alter_table<dd::Table>(old_info, ctx->new_table,
1639 old_dd_tab, new_dd_tab);
1640
2/2
✓ Branch 0 taken 16562 times.
✓ Branch 1 taken 27636 times.
44198 if (!ctx->need_rebuild()) {
1641
1/2
✓ Branch 0 taken 16562 times.
✗ Branch 1 not taken.
16562 dd_commit_inplace_update_instant_meta(ctx->new_table, old_dd_tab,
1642 new_dd_tab);
1643 }
1644
2/4
✓ Branch 0 taken 44198 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44198 times.
44198 ut_ad(dd_table_match(ctx->new_table, new_dd_tab));
1645 }
1646
1647 #ifdef UNIV_DEBUG
1648 /* Inplace ALTERs for expanded fast index creation can only be about
1649 DROP and ADD INDEX and never be instant operation */
1650
7/8
✓ Branch 0 taken 51727 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 451 times.
✓ Branch 3 taken 51276 times.
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 382 times.
✓ Branch 6 taken 388 times.
✓ Branch 7 taken 51339 times.
51796 if (dd_table_has_instant_cols(*old_dd_tab) &&
1651
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 63 times.
69 (ctx == nullptr || !ctx->need_rebuild())) {
1652
3/10
✓ Branch 0 taken 388 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 388 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 388 times.
388 ut_ad(dd_table_has_instant_cols(*new_dd_tab) ||
1653 (ctx == nullptr || ctx->new_table->skip_alter_undo));
1654 }
1655 #endif /* UNIV_DEBUG */
1656
1657 51727 return res;
1658 52119 }
1659
1660 /** Initialize the dict_foreign_t structure with supplied info
1661 @return true if added, false if duplicate foreign->id */
1662 98 static bool innobase_init_foreign(
1663 dict_foreign_t *foreign, /*!< in/out: structure to
1664 initialize */
1665 const char *constraint_name, /*!< in/out: constraint name if
1666 exists */
1667 dict_table_t *table, /*!< in: foreign table */
1668 dict_index_t *index, /*!< in: foreign key index */
1669 const char **column_names, /*!< in: foreign key column
1670 names */
1671 ulint num_field, /*!< in: number of columns */
1672 const char *referenced_table_name, /*!< in: referenced table
1673 name */
1674 dict_table_t *referenced_table, /*!< in: referenced table */
1675 dict_index_t *referenced_index, /*!< in: referenced index */
1676 const char **referenced_column_names, /*!< in: referenced column
1677 names */
1678 ulint referenced_num_field) /*!< in: number of referenced
1679 columns */
1680 {
1681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 ut_ad(dict_sys_mutex_own());
1682
1683
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 if (constraint_name) {
1684 ulint db_len;
1685
1686 /* Catenate 'databasename/' to the constraint name specified
1687 by the user: we conceive the constraint as belonging to the
1688 same MySQL 'database' as the table itself. We store the name
1689 to foreign->id. */
1690
1691 98 db_len = dict_get_db_name_len(table->name.m_name);
1692
1693 196 foreign->id = static_cast<char *>(
1694 98 mem_heap_alloc(foreign->heap, db_len + strlen(constraint_name) + 2));
1695
1696 98 ut_memcpy(foreign->id, table->name.m_name, db_len);
1697 98 foreign->id[db_len] = '/';
1698 98 strcpy(foreign->id + db_len + 1, constraint_name);
1699
1700 /* Check if any existing foreign key has the same id,
1701 this is needed only if user supplies the constraint name */
1702
1703
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 98 times.
98 if (table->foreign_set.find(foreign) != table->foreign_set.end()) {
1704 return (false);
1705 }
1706 }
1707
1708 98 foreign->foreign_table = table;
1709 196 foreign->foreign_table_name =
1710 98 mem_heap_strdup(foreign->heap, table->name.m_name);
1711 98 dict_mem_foreign_table_name_lookup_set(foreign, true);
1712
1713 98 foreign->foreign_index = index;
1714 98 foreign->n_fields = (unsigned int)num_field;
1715
1716 196 foreign->foreign_col_names = static_cast<const char **>(
1717 98 mem_heap_alloc(foreign->heap, num_field * sizeof(void *)));
1718
1719
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 98 times.
214 for (ulint i = 0; i < foreign->n_fields; i++) {
1720 232 foreign->foreign_col_names[i] =
1721 116 mem_heap_strdup(foreign->heap, column_names[i]);
1722 }
1723
1724 98 foreign->referenced_index = referenced_index;
1725 98 foreign->referenced_table = referenced_table;
1726
1727 196 foreign->referenced_table_name =
1728 98 mem_heap_strdup(foreign->heap, referenced_table_name);
1729 98 dict_mem_referenced_table_name_lookup_set(foreign, true);
1730
1731 196 foreign->referenced_col_names = static_cast<const char **>(
1732 98 mem_heap_alloc(foreign->heap, referenced_num_field * sizeof(void *)));
1733
1734
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 98 times.
214 for (ulint i = 0; i < foreign->n_fields; i++) {
1735 232 foreign->referenced_col_names[i] =
1736 116 mem_heap_strdup(foreign->heap, referenced_column_names[i]);
1737 }
1738
1739 98 return (true);
1740 }
1741
1742 /** Check whether the foreign key options is legit
1743 @return true if it is */
1744 98 [[nodiscard]] static bool innobase_check_fk_option(
1745 const dict_foreign_t *foreign) /*!< in: foreign key */
1746 {
1747
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 72 times.
98 if (!foreign->foreign_index) {
1748 26 return (true);
1749 }
1750
1751
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 64 times.
72 if (foreign->type &
1752 (DICT_FOREIGN_ON_UPDATE_SET_NULL | DICT_FOREIGN_ON_DELETE_SET_NULL)) {
1753
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 for (ulint j = 0; j < foreign->n_fields; j++) {
1754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if ((foreign->foreign_index->get_col(j)->prtype) & DATA_NOT_NULL) {
1755 /* It is not sensible to define
1756 SET NULL if the column is not
1757 allowed to be NULL! */
1758 return (false);
1759 }
1760 }
1761 }
1762
1763 72 return (true);
1764 }
1765
1766 /** Set foreign key options
1767 @return true if successfully set */
1768 98 [[nodiscard]] static bool innobase_set_foreign_key_option(
1769 dict_foreign_t *foreign, /*!< in:InnoDB Foreign key */
1770 const Foreign_key_spec *fk_key) /*!< in: Foreign key info from
1771 MySQL */
1772 {
1773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 ut_ad(!foreign->type);
1774
1775
4/5
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 59 times.
✗ Branch 4 not taken.
98 switch (fk_key->delete_opt) {
1776 13 case FK_OPTION_NO_ACTION:
1777 case FK_OPTION_RESTRICT:
1778 case FK_OPTION_DEFAULT:
1779 13 foreign->type = DICT_FOREIGN_ON_DELETE_NO_ACTION;
1780 13 break;
1781 16 case FK_OPTION_CASCADE:
1782 16 foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
1783 16 break;
1784 10 case FK_OPTION_SET_NULL:
1785 10 foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
1786 10 break;
1787 59 case FK_OPTION_UNDEF:
1788 59 break;
1789 }
1790
1791
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
98 switch (fk_key->update_opt) {
1792 case FK_OPTION_NO_ACTION:
1793 case FK_OPTION_RESTRICT:
1794 case FK_OPTION_DEFAULT:
1795 foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
1796 break;
1797 48 case FK_OPTION_CASCADE:
1798 48 foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
1799 48 break;
1800 4 case FK_OPTION_SET_NULL:
1801 4 foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
1802 4 break;
1803 46 case FK_OPTION_UNDEF:
1804 46 break;
1805 }
1806
1807 98 return (innobase_check_fk_option(foreign));
1808 }
1809
1810 /** Check if a foreign key constraint can make use of an index
1811 that is being created.
1812 @return useable index, or NULL if none found */
1813 38 [[nodiscard]] static const KEY *innobase_find_equiv_index(
1814 const char *const *col_names,
1815 /*!< in: column names */
1816 uint n_cols, /*!< in: number of columns */
1817 const KEY *keys, /*!< in: index information */
1818 const uint *add, /*!< in: indexes being created */
1819 uint n_add) /*!< in: number of indexes to create */
1820 {
1821
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 1 times.
41 for (uint i = 0; i < n_add; i++) {
1822 40 const KEY *key = &keys[add[i]];
1823
1824
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
40 if (key->user_defined_key_parts < n_cols || key->flags & HA_SPATIAL) {
1825 no_match:
1826 3 continue;
1827 }
1828
1829
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 37 times.
83 for (uint j = 0; j < n_cols; j++) {
1830 46 const KEY_PART_INFO &key_part = key->key_part[j];
1831 46 uint32_t col_len = key_part.field->pack_length();
1832
1833 /* Any index on virtual columns cannot be used
1834 for reference constaint */
1835
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46 if (innobase_is_v_fld(key_part.field)) {
1836 goto no_match;
1837 }
1838
1839 /* The MySQL pack length contains 1 or 2 bytes
1840 length field for a true VARCHAR. */
1841
1842
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (key_part.field->type() == MYSQL_TYPE_VARCHAR) {
1843 col_len -= key_part.field->get_length_bytes();
1844 }
1845
1846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 if (key_part.length < col_len) {
1847 /* Column prefix indexes cannot be
1848 used for FOREIGN KEY constraints. */
1849 goto no_match;
1850 }
1851
1852
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 43 times.
46 if (innobase_strcasecmp(col_names[j], key_part.field->field_name)) {
1853 /* Name mismatch */
1854 3 goto no_match;
1855 }
1856 }
1857
1858 37 return (key);
1859 }
1860
1861 1 return (nullptr);
1862 }
1863
1864 /** Find an index whose first fields are the columns in the array
1865 in the same order and is not marked for deletion
1866 @return matching index, NULL if not found */
1867 100 [[nodiscard]] static dict_index_t *innobase_find_fk_index(
1868 dict_table_t *table, /*!< in: table */
1869 const char **col_names,
1870 /*!< in: column names, or NULL
1871 to use table->col_names */
1872 dict_index_t **drop_index,
1873 /*!< in: indexes to be dropped */
1874 ulint n_drop_index,
1875 /*!< in: size of drop_index[] */
1876 const char **columns, /*!< in: array of column names */
1877 ulint n_cols) /*!< in: number of columns */
1878 {
1879 dict_index_t *index;
1880
1881 100 index = table->first_index();
1882
1883
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 26 times.
233 while (index != nullptr) {
1884
4/4
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 77 times.
✓ Branch 3 taken 130 times.
413 if (!(index->type & DICT_FTS) &&
1885
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 129 times.
206 dict_foreign_qualify_index(table, col_names, columns, n_cols, index,
1886 nullptr, true, 0)) {
1887
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 74 times.
78 for (ulint i = 0; i < n_drop_index; i++) {
1888
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (index == drop_index[i]) {
1889 /* Skip to-be-dropped indexes. */
1890 3 goto next_rec;
1891 }
1892 }
1893
1894 74 return (index);
1895 }
1896
1897 130 next_rec:
1898 133 index = index->next();
1899 }
1900
1901 26 return (nullptr);
1902 }
1903
1904 /** Check whether given column is a base of stored column.
1905 @param[in] col_name column name
1906 @param[in] table table
1907 @param[in] s_cols list of stored columns
1908 @return true if the given column is a base of stored column,else false. */
1909 4 static bool innobase_col_check_fk(const char *col_name,
1910 const dict_table_t *table,
1911 dict_s_col_list *s_cols) {
1912 4 dict_s_col_list::const_iterator it;
1913
1914
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 for (it = s_cols->begin(); it != s_cols->end(); ++it) {
1915 4 dict_s_col_t s_col = *it;
1916
1917
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 for (ulint j = 0; j < s_col.num_base; j++) {
1918
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (strcmp(col_name, table->get_col_name(s_col.base_col[j]->ind)) == 0) {
1919 1 return (true);
1920 }
1921 }
1922 }
1923
1924 3 return (false);
1925 }
1926
1927 /** Check whether the foreign key constraint is on base of any stored columns.
1928 @param[in] foreign Foriegn key constraing information
1929 @param[in] table table to which the foreign key objects
1930 to be added
1931 @param[in] s_cols list of stored column information in the table.
1932 @return true if yes, otherwise false. */
1933 97 static bool innobase_check_fk_stored(const dict_foreign_t *foreign,
1934 const dict_table_t *table,
1935 dict_s_col_list *s_cols) {
1936 97 ulint type = foreign->type;
1937
1938 97 type &=
1939 ~(DICT_FOREIGN_ON_DELETE_NO_ACTION | DICT_FOREIGN_ON_UPDATE_NO_ACTION);
1940
1941
4/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 4 times.
97 if (type == 0 || s_cols == nullptr) {
1942 93 return (false);
1943 }
1944
1945
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 for (ulint i = 0; i < foreign->n_fields; i++) {
1946
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (innobase_col_check_fk(foreign->foreign_col_names[i], table, s_cols)) {
1947 1 return (true);
1948 }
1949 }
1950
1951 3 return (false);
1952 }
1953
1954 /** Create InnoDB foreign key structure from MySQL alter_info
1955 @param[in] ha_alter_info alter table info
1956 @param[in] table_share TABLE_SHARE
1957 @param[in] table table object
1958 @param[in] col_names column names, or NULL to use
1959 table->col_names
1960 @param[in] drop_index indexes to be dropped
1961 @param[in] n_drop_index size of drop_index
1962 @param[out] add_fk foreign constraint added
1963 @param[out] n_add_fk number of foreign constraints
1964 added
1965 @param[in] trx user transaction
1966 @param[in] s_cols list of stored column information
1967 @retval true if successful
1968 @retval false on error (will call my_error()) */
1969 94 [[nodiscard]] static bool innobase_get_foreign_key_info(
1970 Alter_inplace_info *ha_alter_info, const TABLE_SHARE *table_share,
1971 dict_table_t *table, const char **col_names, dict_index_t **drop_index,
1972 ulint n_drop_index, dict_foreign_t **add_fk, ulint *n_add_fk,
1973 const trx_t *trx, dict_s_col_list *s_cols) {
1974 const Foreign_key_spec *fk_key;
1975 94 dict_table_t *referenced_table = nullptr;
1976 94 char *referenced_table_name = nullptr;
1977 94 ulint num_fk = 0;
1978 94 Alter_info *alter_info = ha_alter_info->alter_info;
1979 MDL_ticket *mdl;
1980
1981
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 DBUG_TRACE;
1982
1983 94 *n_add_fk = 0;
1984
1985
3/4
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 381 times.
✓ Branch 3 taken 90 times.
471 for (const Key_spec *key : alter_info->key_list) {
1986
2/2
✓ Branch 0 taken 281 times.
✓ Branch 1 taken 100 times.
381 if (key->type != KEYTYPE_FOREIGN) {
1987 281 continue;
1988 }
1989
1990 const char *column_names[MAX_NUM_FK_COLUMNS];
1991 100 dict_index_t *index = nullptr;
1992 const char *referenced_column_names[MAX_NUM_FK_COLUMNS];
1993 100 dict_index_t *referenced_index = nullptr;
1994 100 ulint num_col = 0;
1995 100 ulint referenced_num_col = 0;
1996 bool correct_option;
1997 100 char *db_namep = nullptr;
1998 100 char *tbl_namep = nullptr;
1999 100 ulint db_name_len = 0;
2000 100 ulint tbl_name_len = 0;
2001 char db_name[MAX_DATABASE_NAME_LEN];
2002 char tbl_name[MAX_TABLE_NAME_LEN];
2003
2004 100 fk_key = down_cast<const Foreign_key_spec *>(key);
2005
2006
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (fk_key->columns.size() > 0) {
2007 100 size_t i = 0;
2008
2009 /* Get all the foreign key column info for the
2010 current table */
2011
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 100 times.
220 while (i < fk_key->columns.size()) {
2012
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 column_names[i] = fk_key->columns[i]->get_field_name();
2013
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 ut_ad(i < MAX_NUM_FK_COLUMNS);
2014 120 i++;
2015 }
2016
2017
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 index = innobase_find_fk_index(table, col_names, drop_index, n_drop_index,
2018 column_names, i);
2019
2020 /* MySQL would add a index in the creation
2021 list if no such index for foreign table,
2022 so we have to use DBUG_EXECUTE_IF to simulate
2023 the scenario */
2024
3/4
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 99 times.
100 DBUG_EXECUTE_IF("innodb_test_no_foreign_idx", index = nullptr;);
2025
2026 /* Check whether there exist such
2027 index in the the index create clause */
2028
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 73 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 99 times.
127 if (!index &&
2029
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
27 !innobase_find_equiv_index(column_names, static_cast<uint>(i),
2030 27 ha_alter_info->key_info_buffer,
2031
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 ha_alter_info->index_add_buffer,
2032 ha_alter_info->index_add_count)) {
2033
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_FK_NO_INDEX_CHILD, MYF(0),
2034 1 fk_key->name.str ? fk_key->name.str : "",
2035
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 table_share->table_name.str);
2036 4 goto err_exit;
2037 }
2038
2039 99 num_col = i;
2040 }
2041
2042
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 add_fk[num_fk] = dict_mem_foreign_create();
2043
2044 #ifndef _WIN32
2045
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 if (fk_key->ref_db.str) {
2046
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 tablename_to_filename(fk_key->ref_db.str, db_name, MAX_DATABASE_NAME_LEN);
2047 99 db_namep = db_name;
2048 99 db_name_len = strlen(db_name);
2049 }
2050
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 if (fk_key->ref_table.str) {
2051
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 tablename_to_filename(fk_key->ref_table.str, tbl_name,
2052 MAX_TABLE_NAME_LEN);
2053 99 tbl_namep = tbl_name;
2054 99 tbl_name_len = strlen(tbl_name);
2055 }
2056 #else
2057 ut_ad(fk_key->ref_table.str);
2058 tablename_to_filename(fk_key->ref_table.str, tbl_name, MAX_TABLE_NAME_LEN);
2059 innobase_casedn_str(tbl_name);
2060 tbl_name_len = strlen(tbl_name);
2061 tbl_namep = &tbl_name[0];
2062
2063 if (fk_key->ref_db.str != NULL) {
2064 tablename_to_filename(fk_key->ref_db.str, db_name, MAX_DATABASE_NAME_LEN);
2065 innobase_casedn_str(db_name);
2066 db_name_len = strlen(db_name);
2067 db_namep = &db_name[0];
2068 }
2069 #endif
2070
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 dict_sys_mutex_enter();
2071
2072 198 referenced_table_name = dd_get_referenced_table(
2073 99 table->name.m_name, db_namep, db_name_len, tbl_namep, tbl_name_len,
2074
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 &referenced_table, &mdl, add_fk[num_fk]->heap);
2075
2076 /* Test the case when referenced_table failed to
2077 open, if trx->check_foreigns is not set, we should
2078 still be able to add the foreign key */
2079
6/10
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 98 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
99 DBUG_EXECUTE_IF(
2080 "innodb_test_open_ref_fail", if (referenced_table) {
2081 dd_table_close(referenced_table, current_thd, &mdl, true);
2082 referenced_table = nullptr;
2083 });
2084
2085
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
99 if (!referenced_table && trx->check_foreigns) {
2086 dict_sys_mutex_exit();
2087 my_error(ER_FK_CANNOT_OPEN_PARENT, MYF(0), tbl_namep);
2088
2089 goto err_exit;
2090 }
2091
2092
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 if (fk_key->ref_columns.size() > 0) {
2093 99 size_t i = 0;
2094
2095
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 99 times.
217 while (i < fk_key->ref_columns.size()) {
2096
1/2
✓ Branch 0 taken 118 times.
✗ Branch 1 not taken.
118 referenced_column_names[i] = fk_key->ref_columns[i]->get_field_name();
2097
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
118 ut_ad(i < MAX_NUM_FK_COLUMNS);
2098 118 i++;
2099 }
2100
2101
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 27 times.
99 if (referenced_table) {
2102
1/2
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
72 referenced_index = dict_foreign_find_index(referenced_table, nullptr,
2103 referenced_column_names, i,
2104 index, true, false);
2105
2106
3/4
✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 71 times.
72 DBUG_EXECUTE_IF("innodb_test_no_reference_idx",
2107 referenced_index = nullptr;);
2108
2109 /* Check whether there exist such
2110 index in the the index create clause */
2111
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 71 times.
72 if (!referenced_index) {
2112
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 dd_table_close(referenced_table, current_thd, &mdl, true);
2113
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 dict_sys_mutex_exit();
2114
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_FK_NO_INDEX_PARENT, MYF(0),
2115
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fk_key->name.str ? fk_key->name.str : "", tbl_namep);
2116 1 goto err_exit;
2117 }
2118 } else {
2119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 ut_a(!trx->check_foreigns);
2120 }
2121
2122 98 referenced_num_col = i;
2123 } else {
2124 /* Not possible to add a foreign key without a
2125 referenced column */
2126 if (referenced_table) {
2127 dd_table_close(referenced_table, current_thd, &mdl, true);
2128 }
2129 dict_sys_mutex_exit();
2130 my_error(ER_CANNOT_ADD_FOREIGN, MYF(0), tbl_namep);
2131 goto err_exit;
2132 }
2133
2134
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 98 times.
98 if (!innobase_init_foreign(add_fk[num_fk], fk_key->name.str, table, index,
2135 column_names, num_col, referenced_table_name,
2136 referenced_table, referenced_index,
2137 referenced_column_names, referenced_num_col)) {
2138 if (referenced_table) {
2139 dd_table_close(referenced_table, current_thd, &mdl, true);
2140 }
2141 dict_sys_mutex_exit();
2142 my_error(ER_FK_DUP_NAME, MYF(0), add_fk[num_fk]->id);
2143 goto err_exit;
2144 }
2145
2146
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 27 times.
98 if (referenced_table) {
2147
2/4
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 71 times.
✗ Branch 3 not taken.
71 dd_table_close(referenced_table, current_thd, &mdl, true);
2148 }
2149
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 dict_sys_mutex_exit();
2150
2151
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 correct_option = innobase_set_foreign_key_option(add_fk[num_fk], fk_key);
2152
2153
3/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 97 times.
98 DBUG_EXECUTE_IF("innodb_test_wrong_fk_option", correct_option = false;);
2154
2155
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 97 times.
98 if (!correct_option) {
2156 1 my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_share->table_name.str,
2157
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 add_fk[num_fk]->id);
2158 1 goto err_exit;
2159 }
2160
2161
3/4
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 96 times.
97 if (innobase_check_fk_stored(add_fk[num_fk], table, s_cols)) {
2162
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_CANNOT_ADD_FOREIGN_BASE_COL_STORED, MYF(0));
2163 1 goto err_exit;
2164 }
2165
2166 96 num_fk++;
2167 }
2168
2169 90 *n_add_fk = num_fk;
2170
2171 90 return true;
2172 4 err_exit:
2173
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (ulint i = 0; i <= num_fk; i++) {
2174
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (add_fk[i]) {
2175
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 dict_foreign_free(add_fk[i]);
2176 }
2177 }
2178
2179 4 return false;
2180 94 }
2181
2182 /** Copies an InnoDB column to a MySQL field. This function is
2183 adapted from row_sel_field_store_in_mysql_format(). */
2184 98 static void innobase_col_to_mysql(
2185 const dict_col_t *col, /*!< in: InnoDB column */
2186 const uchar *data, /*!< in: InnoDB column data */
2187 ulint len, /*!< in: length of data, in bytes */
2188 Field *field) /*!< in/out: MySQL field */
2189 {
2190 uchar *ptr;
2191 98 uchar *dest = field->field_ptr();
2192 98 ulint flen = field->pack_length();
2193
2194
5/7
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
98 switch (col->mtype) {
2195 55 case DATA_INT:
2196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 ut_ad(len == flen);
2197
2198 /* Convert integer data from Innobase to little-endian
2199 format, sign bit restored to normal */
2200
2201
2/2
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 55 times.
282 for (ptr = dest + len; ptr != dest;) {
2202 227 *--ptr = *data++;
2203 }
2204
2205
1/2
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
55 if (!field->is_flag_set(UNSIGNED_FLAG)) {
2206 55 ((byte *)dest)[len - 1] ^= 0x80;
2207 }
2208
2209 55 break;
2210
2211 8 case DATA_VARCHAR:
2212 case DATA_VARMYSQL:
2213 case DATA_BINARY:
2214 8 field->reset();
2215
2216
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (field->type() == MYSQL_TYPE_VARCHAR) {
2217 /* This is a >= 5.0.3 type true VARCHAR. Store the
2218 length of the data to the first byte or the first
2219 two bytes of dest. */
2220
2221 dest =
2222 8 row_mysql_store_true_var_len(dest, len, flen - field->key_length());
2223 }
2224
2225 /* Copy the actual data */
2226 8 memcpy(dest, data, len);
2227 8 break;
2228
2229 17 case DATA_VAR_POINT:
2230 case DATA_GEOMETRY:
2231 case DATA_BLOB:
2232 /* Skip MySQL BLOBs when reporting an erroneous row
2233 during index creation or table rebuild. */
2234 17 field->set_null();
2235 17 break;
2236
2237 #ifdef UNIV_DEBUG
2238 4 case DATA_MYSQL:
2239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(flen >= len);
2240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(DATA_MBMAXLEN(col->mbminmaxlen) >= DATA_MBMINLEN(col->mbminmaxlen));
2241 4 memcpy(dest, data, len);
2242 4 break;
2243
2244 default:
2245 case DATA_SYS_CHILD:
2246 case DATA_SYS:
2247 /* These column types should never be shipped to MySQL. */
2248 ut_d(ut_error);
2249 [[fallthrough]];
2250
2251 case DATA_FLOAT:
2252 case DATA_DOUBLE:
2253 case DATA_DECIMAL:
2254 case DATA_POINT:
2255 /* Above are the valid column types for MySQL data. */
2256 ut_ad(flen == len);
2257 [[fallthrough]];
2258 case DATA_FIXBINARY:
2259 case DATA_CHAR:
2260 /* We may have flen > len when there is a shorter
2261 prefix on the CHAR and BINARY column. */
2262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
14 ut_ad(flen >= len);
2263 #else /* UNIV_DEBUG */
2264 default:
2265 #endif /* UNIV_DEBUG */
2266 15 memcpy(dest, data, len);
2267 }
2268 99 }
2269
2270 /** Copies an InnoDB record to table->record[0].
2271 @param[in,out] table Mysql table
2272 @param[in] rec Record
2273 @param[in] index Index
2274 @param[in] offsets rec_get_offsets( rec, index, ...) */
2275 void innobase_rec_to_mysql(struct TABLE *table, const rec_t *rec,
2276 const dict_index_t *index, const ulint *offsets) {
2277 uint n_fields = table->s->fields;
2278
2279 ut_ad(n_fields ==
2280 dict_table_get_n_tot_u_cols(index->table) -
2281 DICT_TF2_FLAG_IS_SET(index->table, DICT_TF2_FTS_HAS_DOC_ID));
2282
2283 for (uint i = 0; i < n_fields; i++) {
2284 Field *field = table->field[i];
2285 ulint ipos;
2286 ulint ilen;
2287 const uchar *ifield;
2288
2289 field->reset();
2290
2291 ipos = index->get_col_pos(i, true, false, nullptr);
2292
2293 if (ipos == ULINT_UNDEFINED || rec_offs_nth_extern(index, offsets, ipos)) {
2294 null_field:
2295 field->set_null();
2296 continue;
2297 }
2298
2299 ifield = rec_get_nth_field_instant(rec, offsets, ipos, index, &ilen);
2300
2301 /* Assign the NULL flag */
2302 if (ilen == UNIV_SQL_NULL) {
2303 ut_ad(field->is_nullable());
2304 goto null_field;
2305 }
2306
2307 field->set_notnull();
2308
2309 innobase_col_to_mysql(index->get_field(ipos)->col, ifield, ilen, field);
2310 }
2311 }
2312
2313 /** Copies an InnoDB index entry to table->record[0].
2314 @param[in,out] table Mysql table
2315 @param[in] index Innodb index
2316 @param[in] fields Innodb index fields */
2317 39 void innobase_fields_to_mysql(struct TABLE *table, const dict_index_t *index,
2318 const dfield_t *fields) {
2319 39 uint n_fields = table->s->fields;
2320 39 ulint num_v = 0;
2321
2322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 ut_ad(n_fields ==
2323 index->table->get_n_user_cols() +
2324 dict_table_get_n_v_cols(index->table) -
2325 DICT_TF2_FLAG_IS_SET(index->table, DICT_TF2_FTS_HAS_DOC_ID));
2326
2327
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 39 times.
157 for (uint i = 0; i < n_fields; i++) {
2328 118 Field *field = table->field[i];
2329 ulint ipos;
2330 ulint col_n;
2331
2332 118 field->reset();
2333
2334
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
117 if (innobase_is_v_fld(field)) {
2335 1 col_n = num_v;
2336 1 num_v++;
2337 } else {
2338 116 col_n = i - num_v;
2339 }
2340
2341
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
117 ipos = index->get_col_pos(col_n, true, innobase_is_v_fld(field), nullptr);
2342
2343
6/6
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 90 times.
207 if (ipos == ULINT_UNDEFINED || dfield_is_ext(&fields[ipos]) ||
2344
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 dfield_is_null(&fields[ipos])) {
2345 27 field->set_null();
2346 } else {
2347 90 field->set_notnull();
2348
2349 90 const dfield_t *df = &fields[ipos];
2350
2351 90 innobase_col_to_mysql(index->get_field(ipos)->col,
2352 90 static_cast<const uchar *>(dfield_get_data(df)),
2353 90 dfield_get_len(df), field);
2354 }
2355 }
2356 39 }
2357
2358 /** Copies an InnoDB row to table->record[0].
2359 @param[in,out] table Mysql table
2360 @param[in] itab Innodb table
2361 @param[in] row Innodb row */
2362 5 void innobase_row_to_mysql(struct TABLE *table, const dict_table_t *itab,
2363 const dtuple_t *row) {
2364 5 uint n_fields = table->s->fields;
2365 5 ulint num_v = 0;
2366
2367 /* The InnoDB row may contain an extra FTS_DOC_ID column at the end. */
2368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 ut_ad(row->n_fields == itab->get_n_cols());
2369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 ut_ad(n_fields == row->n_fields - DATA_N_SYS_COLS +
2370 dict_table_get_n_v_cols(itab) -
2371 DICT_TF2_FLAG_IS_SET(itab, DICT_TF2_FTS_HAS_DOC_ID));
2372
2373
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 for (uint i = 0; i < n_fields; i++) {
2374 13 Field *field = table->field[i];
2375
2376 13 field->reset();
2377
2378
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
13 if (innobase_is_v_fld(field)) {
2379 /* Virtual column are not stored in InnoDB table, so
2380 skip it */
2381 2 num_v++;
2382 2 continue;
2383 }
2384
2385 11 const dfield_t *df = dtuple_get_nth_field(row, i - num_v);
2386
2387
5/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 8 times.
11 if (dfield_is_ext(df) || dfield_is_null(df)) {
2388 3 field->set_null();
2389 } else {
2390 8 field->set_notnull();
2391
2392 8 innobase_col_to_mysql(itab->get_col(i - num_v),
2393 8 static_cast<const uchar *>(dfield_get_data(df)),
2394 8 dfield_get_len(df), field);
2395 }
2396 }
2397 5 }
2398
2399 /** Resets table->record[0]. */
2400 35473 void innobase_rec_reset(TABLE *table) /*!< in/out: MySQL table */
2401 {
2402 35473 uint n_fields = table->s->fields;
2403 uint i;
2404
2405
2/2
✓ Branch 0 taken 270015 times.
✓ Branch 1 taken 35473 times.
305488 for (i = 0; i < n_fields; i++) {
2406
2/2
✓ Branch 0 taken 269989 times.
✓ Branch 1 taken 26 times.
270015 if (!table->field[i]->m_default_val_expr) {
2407 269989 table->field[i]->set_default();
2408 } else {
2409 26 table->field[i]->copy_data(table->default_values_offset());
2410 }
2411 }
2412 35473 }
2413
2414 /** This function checks that index keys are sensible.
2415 @return 0 or error number */
2416 47715 [[nodiscard]] static int innobase_check_index_keys(
2417 const Alter_inplace_info *info,
2418 /*!< in: indexes to be created or dropped */
2419 const dict_table_t *innodb_table)
2420 /*!< in: Existing indexes */
2421 {
2422
2/2
✓ Branch 0 taken 7813 times.
✓ Branch 1 taken 47715 times.
55528 for (uint key_num = 0; key_num < info->index_add_count; key_num++) {
2423 7813 const KEY &key = info->key_info_buffer[info->index_add_buffer[key_num]];
2424
2425 /* Check that the same index name does not appear
2426 twice in indexes to be created. */
2427
2428
2/2
✓ Branch 0 taken 2223 times.
✓ Branch 1 taken 7813 times.
10036 for (ulint i = 0; i < key_num; i++) {
2429 2223 const KEY &key2 = info->key_info_buffer[info->index_add_buffer[i]];
2430
2431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2223 times.
2223 if (0 == strcmp(key.name, key2.name)) {
2432 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key.name);
2433
2434 return (ER_WRONG_NAME_FOR_INDEX);
2435 }
2436 }
2437
2438 /* Check that the same index name does not already exist. */
2439
2440 const dict_index_t *index;
2441
2442
2/2
✓ Branch 0 taken 12472 times.
✓ Branch 1 taken 7071 times.
19543 for (index = innodb_table->first_index(); index; index = index->next()) {
2443
6/6
✓ Branch 0 taken 12470 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 742 times.
✓ Branch 3 taken 11728 times.
✓ Branch 4 taken 742 times.
✓ Branch 5 taken 11730 times.
12472 if (index->is_committed() && !strcmp(key.name, index->name)) {
2444 742 break;
2445 }
2446 }
2447
2448 /* Now we are in a situation where we have "ADD INDEX x"
2449 and an index by the same name already exists. We have 4
2450 possible cases:
2451 1. No further clauses for an index x are given. Should reject
2452 the operation.
2453 2. "DROP INDEX x" is given. Should allow the operation.
2454 3. "RENAME INDEX x TO y" is given. Should allow the operation.
2455 4. "DROP INDEX x, RENAME INDEX x TO y" is given. Should allow
2456 the operation, since no name clash occurs. In this particular
2457 case MySQL cancels the operation without calling InnoDB
2458 methods. */
2459
2460
2/2
✓ Branch 0 taken 742 times.
✓ Branch 1 taken 7071 times.
7813 if (index) {
2461 /* If a key by the same name is being created and
2462 dropped, the name clash is OK. */
2463
2/2
✓ Branch 0 taken 764 times.
✓ Branch 1 taken 8 times.
772 for (uint i = 0; i < info->index_drop_count; i++) {
2464 764 const KEY *drop_key = info->index_drop_buffer[i];
2465
2466
2/2
✓ Branch 0 taken 734 times.
✓ Branch 1 taken 30 times.
764 if (0 == strcmp(key.name, drop_key->name)) {
2467 734 goto name_ok;
2468 }
2469 }
2470
2471 /* If a key by the same name is being created and
2472 renamed, the name clash is OK. E.g.
2473 ALTER TABLE t ADD INDEX i (col), RENAME INDEX i TO x
2474 where the index "i" exists prior to the ALTER command.
2475 In this case we:
2476 1. rename the existing index from "i" to "x"
2477 2. add the new index "i" */
2478
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 for (uint i = 0; i < info->index_rename_count; i++) {
2479 8 const KEY_PAIR *pair = &info->index_rename_buffer[i];
2480
2481
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (0 == strcmp(key.name, pair->old_key->name)) {
2482 8 goto name_ok;
2483 }
2484 }
2485
2486 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key.name);
2487
2488 return (ER_WRONG_NAME_FOR_INDEX);
2489 }
2490
2491 7071 name_ok:
2492
2/2
✓ Branch 0 taken 11743 times.
✓ Branch 1 taken 7813 times.
19556 for (ulint i = 0; i < key.user_defined_key_parts; i++) {
2493 11743 const KEY_PART_INFO &key_part1 = key.key_part[i];
2494 11743 const Field *field = key_part1.field;
2495 ulint is_unsigned;
2496
2497
3/4
✓ Branch 0 taken 11743 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5592 times.
✓ Branch 3 taken 6151 times.
11743 switch (get_innobase_type_from_mysql_type(&is_unsigned, field)) {
2498 5592 default:
2499 5592 break;
2500 6151 case DATA_INT:
2501 case DATA_FLOAT:
2502 case DATA_DOUBLE:
2503 case DATA_DECIMAL:
2504 /* Check that MySQL does not try to
2505 create a column prefix index field on
2506 an inappropriate data type. */
2507
2508
2/4
✓ Branch 0 taken 6151 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6151 times.
6151 if (field->type() == MYSQL_TYPE_VARCHAR) {
2509 if (key_part1.length >=
2510 field->pack_length() - field->get_length_bytes()) {
2511 break;
2512 }
2513 } else {
2514
4/6
✓ Branch 0 taken 6151 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 6105 times.
✓ Branch 4 taken 6151 times.
✗ Branch 5 not taken.
6197 if (key_part1.length >= field->pack_length() ||
2515
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 innobase_is_multi_value_fld(field)) {
2516 6151 break;
2517 }
2518 }
2519
2520 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
2521 return (ER_WRONG_KEY_COLUMN);
2522 }
2523
2524 /* Check that the same column does not appear
2525 twice in the index. */
2526
2527
2/2
✓ Branch 0 taken 13540 times.
✓ Branch 1 taken 11743 times.
25283 for (ulint j = 0; j < i; j++) {
2528 13540 const KEY_PART_INFO &key_part2 = key.key_part[j];
2529
2530
1/2
✓ Branch 0 taken 13540 times.
✗ Branch 1 not taken.
13540 if (key_part1.fieldnr != key_part2.fieldnr) {
2531 13540 continue;
2532 }
2533
2534 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
2535 return (ER_WRONG_KEY_COLUMN);
2536 }
2537 }
2538 }
2539
2540 47715 return (0);
2541 }
2542
2543 /** Create index field definition for key part
2544 @param[in] altered_table MySQL table that is being altered,
2545 or NULL if a new clustered index
2546 is not being created
2547 @param[in] key_part MySQL key definition
2548 @param[in,out] index_field index field
2549 @param[in] new_clustered new cluster */
2550 60531 static void innobase_create_index_field_def(const TABLE *altered_table,
2551 const KEY_PART_INFO *key_part,
2552 ddl::Index_field *index_field,
2553 bool new_clustered) {
2554 const Field *field;
2555 ulint is_unsigned;
2556 ulint col_type;
2557 60531 ulint num_v = 0;
2558
2559
1/2
✓ Branch 0 taken 60531 times.
✗ Branch 1 not taken.
60531 DBUG_TRACE;
2560
2561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60531 times.
60531 ut_ad(key_part);
2562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60531 times.
60531 ut_ad(index_field);
2563
2564
2/2
✓ Branch 0 taken 53383 times.
✓ Branch 1 taken 7148 times.
60531 field =
2565 53383 new_clustered ? altered_table->field[key_part->fieldnr] : key_part->field;
2566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60531 times.
60531 ut_a(field);
2567
2568
2/2
✓ Branch 0 taken 111609 times.
✓ Branch 1 taken 60531 times.
172140 for (ulint i = 0; i < key_part->fieldnr; i++) {
2569
4/4
✓ Branch 0 taken 372 times.
✓ Branch 1 taken 111237 times.
✓ Branch 2 taken 277 times.
✓ Branch 3 taken 95 times.
111609 if (innobase_is_v_fld(altered_table->field[i])) {
2570 277 num_v++;
2571 }
2572 }
2573
2574
1/2
✓ Branch 0 taken 60531 times.
✗ Branch 1 not taken.
60531 col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
2575
2576
1/2
✓ Branch 0 taken 60531 times.
✗ Branch 1 not taken.
60531 index_field->m_is_multi_value = innobase_is_multi_value_fld(field);
2577
3/4
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 60181 times.
✓ Branch 2 taken 350 times.
✗ Branch 3 not taken.
60531 if (!field->stored_in_db && field->gcol_info) {
2578 350 index_field->m_is_v_col = true;
2579 350 index_field->m_col_no = num_v;
2580 } else {
2581 60181 index_field->m_is_v_col = false;
2582 60181 index_field->m_col_no = key_part->fieldnr - num_v;
2583 }
2584 60531 index_field->m_is_ascending = !(key_part->key_part_flag & HA_REVERSE_SORT);
2585
2586 /* No prefix index on multi-value field */
2587
6/6
✓ Branch 0 taken 60433 times.
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 59720 times.
✓ Branch 3 taken 713 times.
✓ Branch 4 taken 1077 times.
✓ Branch 5 taken 59454 times.
180684 if (!index_field->m_is_multi_value &&
2588 60433 (DATA_LARGE_MTYPE(col_type) ||
2589
3/4
✓ Branch 0 taken 59720 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5492 times.
✓ Branch 3 taken 54228 times.
59720 (key_part->length < field->pack_length() &&
2590
3/4
✓ Branch 0 taken 5492 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5410 times.
✓ Branch 3 taken 82 times.
5492 field->type() != MYSQL_TYPE_VARCHAR) ||
2591
3/4
✓ Branch 0 taken 59638 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5410 times.
✓ Branch 3 taken 54228 times.
59638 (field->type() == MYSQL_TYPE_VARCHAR &&
2592
4/6
✓ Branch 0 taken 5410 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5410 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 282 times.
✓ Branch 5 taken 5128 times.
5410 key_part->length < field->pack_length() - field->get_length_bytes()))) {
2593 1077 index_field->m_prefix_len = key_part->length;
2594 } else {
2595 59454 index_field->m_prefix_len = 0;
2596 }
2597 60531 }
2598
2599 template <typename Index>
2600 const dd::Index *get_dd_index(const Index *index);
2601
2602 template <>
2603 105 const dd::Index *get_dd_index<dd::Index>(const dd::Index *dd_index) {
2604 105 return dd_index;
2605 }
2606
2607 template <>
2608 const dd::Index *get_dd_index<dd::Partition_index>(
2609 const dd::Partition_index *dd_index) {
2610 return (dd_index != nullptr) ? &dd_index->index() : nullptr;
2611 }
2612
2613 /** Create index definition for key
2614 @param[in] altered_table MySQL table that is being altered
2615 @param[in] new_dd_tab New dd table
2616 @param[in] keys Key definitions
2617 @param[in] key_number MySQL key number
2618 @param[in] new_clustered true if generating a new clustered index
2619 on the table
2620 @param[in] key_clustered true if this is the new clustered index
2621 @param[out] index_def Index definition
2622 @param[in] heap heap where memory is allocated */
2623 template <typename Table>
2624 77380 static void innobase_create_index_def(const TABLE *altered_table,
2625 const Table *new_dd_tab, const KEY *keys,
2626 ulint key_number, bool new_clustered,
2627 bool key_clustered,
2628 ddl::Index_defn *index_def,
2629 mem_heap_t *heap) {
2630 ulint i;
2631 77380 const KEY *key = &keys[key_number];
2632 77380 ulint n_fields = key->user_defined_key_parts;
2633
2634
1/2
✓ Branch 0 taken 38690 times.
✗ Branch 1 not taken.
77380 DBUG_TRACE;
2635
3/4
✓ Branch 0 taken 23718 times.
✓ Branch 1 taken 14972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23718 times.
77380 assert(!key_clustered || new_clustered);
2636
2637 77380 index_def->m_fields = static_cast<ddl::Index_field *>(
2638
1/2
✓ Branch 0 taken 38690 times.
✗ Branch 1 not taken.
77380 mem_heap_alloc(heap, n_fields * sizeof *index_def->m_fields));
2639
2640 77380 index_def->m_parser = nullptr;
2641 77380 index_def->m_is_ngram = false;
2642 77380 index_def->m_key_number = key_number;
2643 77380 index_def->m_n_fields = n_fields;
2644
1/2
✓ Branch 0 taken 38690 times.
✗ Branch 1 not taken.
77380 index_def->m_name = mem_heap_strdup(heap, key->name);
2645 77380 index_def->m_rebuild = new_clustered;
2646
2647 /* If this is a spatial index, we need to fetch the SRID */
2648
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 38585 times.
77380 if (key->flags & HA_SPATIAL) {
2649 210 ulint dd_key_num =
2650
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 47 times.
210 key_number + ((altered_table->s->primary_key == MAX_KEY) ? 1 : 0);
2651
2652 210 const auto *dd_index_auto =
2653 210 (index_def->m_key_number != ULINT_UNDEFINED)
2654
3/6
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 105 times.
✗ Branch 5 not taken.
210 ? const_cast<const Table *>(new_dd_tab)->indexes()[dd_key_num]
2655 : nullptr;
2656
2657
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
210 const dd::Index *dd_index = get_dd_index(dd_index_auto);
2658
2659
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
210 if (dd_index != nullptr) {
2660
2/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 105 times.
210 ut_ad(dd_index->name() == key->name);
2661 /* Spatial index indexes on only one column */
2662 size_t geom_col_idx;
2663
2/4
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
210 for (geom_col_idx = 0; geom_col_idx < dd_index->elements().size();
2664 ++geom_col_idx) {
2665
5/10
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 105 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 105 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 105 times.
✗ Branch 9 not taken.
210 if (!dd_index->elements()[geom_col_idx]->column().is_se_hidden()) break;
2666 }
2667
3/6
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 105 times.
✗ Branch 5 not taken.
210 const dd::Column &col = dd_index->elements()[geom_col_idx]->column();
2668
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
210 bool has_value = col.srs_id().has_value();
2669 210 index_def->m_srid_is_valid = has_value;
2670
4/6
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
210 index_def->m_srid = has_value ? col.srs_id().value() : 0;
2671 }
2672 }
2673
2674
2/2
✓ Branch 0 taken 23718 times.
✓ Branch 1 taken 14972 times.
77380 if (key_clustered) {
2675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23718 times.
47436 assert(!(key->flags & (HA_FULLTEXT | HA_SPATIAL)));
2676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23718 times.
47436 assert(key->flags & HA_NOSAME);
2677 47436 index_def->m_ind_type = DICT_CLUSTERED | DICT_UNIQUE;
2678
2/2
✓ Branch 0 taken 469 times.
✓ Branch 1 taken 14503 times.
29944 } else if (key->flags & HA_FULLTEXT) {
2679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 469 times.
938 assert(!(key->flags & (HA_SPATIAL | HA_NOSAME)));
2680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 469 times.
938 assert(!(key->flags & HA_KEYFLAG_MASK &
2681 ~(HA_FULLTEXT | HA_PACK_KEY | HA_BINARY_PACK_KEY)));
2682 938 index_def->m_ind_type = DICT_FTS;
2683
2684 /* Set plugin parser */
2685 /* Note: key->parser is only parser name,
2686 we need to get parser from altered_table instead */
2687
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 230 times.
938 if (key->flags & HA_USES_PARSER) {
2688
1/2
✓ Branch 0 taken 1152 times.
✗ Branch 1 not taken.
2304 for (ulint j = 0; j < altered_table->s->keys; j++) {
2689
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 913 times.
2304 if (ut_strcmp(altered_table->key_info[j].name, key->name) == 0) {
2690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 239 times.
478 ut_ad(altered_table->key_info[j].flags & HA_USES_PARSER);
2691
2692 478 plugin_ref parser = altered_table->key_info[j].parser;
2693 478 index_def->m_parser =
2694 478 static_cast<st_mysql_ftparser *>(plugin_decl(parser)->info);
2695
2696 478 index_def->m_is_ngram =
2697 478 strncmp(plugin_name(parser)->str, FTS_NGRAM_PARSER_NAME,
2698 478 plugin_name(parser)->length) == 0;
2699
2700 478 break;
2701 }
2702 }
2703
2704
3/4
✓ Branch 0 taken 239 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 238 times.
478 DBUG_EXECUTE_IF("fts_instrument_use_default_parser",
2705 index_def->m_parser = &fts_default_parser;);
2706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 239 times.
478 ut_ad(index_def->m_parser);
2707 }
2708
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 14398 times.
29006 } else if (key->flags & HA_SPATIAL) {
2709
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
210 assert(!(key->flags & HA_NOSAME));
2710 210 index_def->m_ind_type = DICT_SPATIAL;
2711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
210 ut_ad(n_fields == 1);
2712 210 ulint num_v = 0;
2713
2714 /* Need to count the virtual fields before this spatial
2715 indexed field */
2716
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 105 times.
468 for (ulint i = 0; i < key->key_part->fieldnr; i++) {
2717
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 3 times.
258 if (innobase_is_v_fld(altered_table->field[i])) {
2718 28 num_v++;
2719 }
2720 }
2721 210 index_def->m_fields[0].m_col_no = key->key_part[0].fieldnr - num_v;
2722 210 index_def->m_fields[0].m_prefix_len = 0;
2723 210 index_def->m_fields[0].m_is_v_col = false;
2724
2725 /* Currently only ascending order is supported in spatial
2726 index. */
2727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
210 ut_ad(!(key->key_part[0].key_part_flag & HA_REVERSE_SORT));
2728 210 index_def->m_fields[0].m_is_ascending = true;
2729
2730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
210 if (!key->key_part[0].field->stored_in_db &&
2731 key->key_part[0].field->gcol_info) {
2732 index_def->m_fields[0].m_is_v_col = true;
2733 /* Currently, the spatial index cannot be created
2734 on virtual columns. It is blocked in server
2735 layer */
2736 ut_d(ut_error);
2737 } else {
2738 210 index_def->m_fields[0].m_is_v_col = false;
2739 }
2740 } else {
2741 28796 index_def->m_ind_type = (key->flags & HA_NOSAME) ? DICT_UNIQUE : 0;
2742 }
2743
2744
2/2
✓ Branch 0 taken 38585 times.
✓ Branch 1 taken 105 times.
77380 if (!(key->flags & HA_SPATIAL)) {
2745
2/2
✓ Branch 0 taken 60531 times.
✓ Branch 1 taken 38585 times.
198232 for (i = 0; i < n_fields; i++) {
2746 121062 innobase_create_index_field_def(altered_table, &key->key_part[i],
2747
1/2
✓ Branch 0 taken 60531 times.
✗ Branch 1 not taken.
121062 &index_def->m_fields[i], new_clustered);
2748
2749
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 60181 times.
121062 if (index_def->m_fields[i].m_is_v_col) {
2750 700 index_def->m_ind_type |= DICT_VIRTUAL;
2751 }
2752
2753
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 60433 times.
121062 if (index_def->m_fields[i].m_is_multi_value) {
2754 196 index_def->m_ind_type |= DICT_MULTI_VALUE;
2755 }
2756 }
2757 }
2758 77380 }
2759
2760 /** Check whether the table has the FTS_DOC_ID column
2761 @return whether there exists an FTS_DOC_ID column */
2762 491 bool innobase_fts_check_doc_id_col(
2763 const dict_table_t *table, /*!< in: InnoDB table with
2764 fulltext index */
2765 const TABLE *altered_table,
2766 /*!< in: MySQL table with
2767 fulltext index */
2768 ulint *fts_doc_col_no,
2769 /*!< out: The column number for
2770 Doc ID, or ULINT_UNDEFINED
2771 if it is of wrong type */
2772 ulint *num_v) /*!< out: number of virtual column */
2773 {
2774 491 *fts_doc_col_no = ULINT_UNDEFINED;
2775
2776 491 const uint n_cols = altered_table->s->fields;
2777 ulint i;
2778
2779 491 *num_v = 0;
2780
2781
2/2
✓ Branch 0 taken 2901 times.
✓ Branch 1 taken 454 times.
3355 for (i = 0; i < n_cols; i++) {
2782 2901 const Field *field = altered_table->field[i];
2783
2784
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2895 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
2901 if (innobase_is_v_fld(field)) {
2785 4 (*num_v)++;
2786 }
2787
2788
2/2
✓ Branch 0 taken 2864 times.
✓ Branch 1 taken 37 times.
2901 if (my_strcasecmp(system_charset_info, field->field_name,
2789 FTS_DOC_ID_COL_NAME)) {
2790 2864 continue;
2791 }
2792
2793
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (strcmp(field->field_name, FTS_DOC_ID_COL_NAME)) {
2794 my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name);
2795 37 } else if (field->type() != MYSQL_TYPE_LONGLONG ||
2796
2/4
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
37 field->pack_length() != 8 || field->is_nullable() ||
2797
6/10
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 36 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 36 times.
74 !field->is_flag_set(UNSIGNED_FLAG) || innobase_is_v_fld(field)) {
2798 1 my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN, MYF(0), field->field_name);
2799 } else {
2800 36 *fts_doc_col_no = i - *num_v;
2801 }
2802
2803 37 return (true);
2804 }
2805
2806
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 454 times.
454 if (!table) {
2807 return (false);
2808 }
2809
2810 /* Not to count the virtual columns */
2811 454 i -= *num_v;
2812
2813
2/2
✓ Branch 0 taken 332 times.
✓ Branch 1 taken 122 times.
454 for (; i + DATA_N_SYS_COLS < (uint)table->n_cols; i++) {
2814 332 const char *name = table->get_col_name(i);
2815
2816
1/2
✓ Branch 0 taken 332 times.
✗ Branch 1 not taken.
332 if (strcmp(name, FTS_DOC_ID_COL_NAME) == 0) {
2817 #ifdef UNIV_DEBUG
2818 const dict_col_t *col;
2819
2820 332 col = table->get_col(i);
2821
2822 /* Because the FTS_DOC_ID does not exist in
2823 the MySQL data dictionary, this must be the
2824 internally created FTS_DOC_ID column. */
2825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332 times.
332 ut_ad(col->mtype == DATA_INT);
2826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332 times.
332 ut_ad(col->len == 8);
2827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332 times.
332 ut_ad(col->prtype & DATA_NOT_NULL);
2828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332 times.
332 ut_ad(col->prtype & DATA_UNSIGNED);
2829 #endif /* UNIV_DEBUG */
2830 332 *fts_doc_col_no = i;
2831 332 return (true);
2832 }
2833 }
2834
2835 122 return (false);
2836 }
2837
2838 /** Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2839 on the Doc ID column.
2840 @return the status of the FTS_DOC_ID index */
2841 6653 enum fts_doc_id_index_enum innobase_fts_check_doc_id_index(
2842 const dict_table_t *table, /*!< in: table definition */
2843 const TABLE *altered_table, /*!< in: MySQL table
2844 that is being altered */
2845 ulint *fts_doc_col_no) /*!< out: The column number for
2846 Doc ID, or ULINT_UNDEFINED
2847 if it is being created in
2848 ha_alter_info */
2849 {
2850 const dict_index_t *index;
2851 const dict_field_t *field;
2852
2853
2/2
✓ Branch 0 taken 489 times.
✓ Branch 1 taken 6164 times.
6653 if (altered_table) {
2854 /* Check if a unique index with the name of
2855 FTS_DOC_ID_INDEX_NAME is being created. */
2856
2857
2/2
✓ Branch 0 taken 1773 times.
✓ Branch 1 taken 473 times.
2246 for (uint i = 0; i < altered_table->s->keys; i++) {
2858 1773 const KEY &key = altered_table->key_info[i];
2859
2860
2/2
✓ Branch 0 taken 1757 times.
✓ Branch 1 taken 16 times.
1773 if (innobase_strcasecmp(key.name, FTS_DOC_ID_INDEX_NAME)) {
2861 1757 continue;
2862 }
2863
2864
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if ((key.flags & HA_NOSAME) &&
2865
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 key.user_defined_key_parts == 1
2866 /* For now, we do not allow a descending index,
2867 because fts_doc_fetch_by_doc_id() uses the
2868 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2869
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 && !(key.key_part[0].key_part_flag & HA_REVERSE_SORT) &&
2870
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 !strcmp(key.name, FTS_DOC_ID_INDEX_NAME) &&
2871
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 !strcmp(key.key_part[0].field->field_name, FTS_DOC_ID_COL_NAME)) {
2872
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (fts_doc_col_no) {
2873 16 *fts_doc_col_no = ULINT_UNDEFINED;
2874 }
2875 16 return (FTS_EXIST_DOC_ID_INDEX);
2876 } else {
2877 return (FTS_INCORRECT_DOC_ID_INDEX);
2878 }
2879 }
2880 }
2881
2882
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6637 times.
6637 if (!table) {
2883 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2884 }
2885
2886
2/2
✓ Branch 0 taken 16228 times.
✓ Branch 1 taken 137 times.
16365 for (index = table->first_index(); index; index = index->next()) {
2887 /* Check if there exists a unique index with the name of
2888 FTS_DOC_ID_INDEX_NAME */
2889
2/2
✓ Branch 0 taken 9728 times.
✓ Branch 1 taken 6500 times.
16228 if (innobase_strcasecmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2890 9728 continue;
2891 }
2892
2893 6500 if (!dict_index_is_unique(index) ||
2894
1/2
✓ Branch 0 taken 6500 times.
✗ Branch 1 not taken.
6500 dict_index_get_n_unique(index) > 1
2895 /* For now, we do not allow a descending index,
2896 because fts_doc_fetch_by_doc_id() uses the
2897 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2898
3/6
✓ Branch 0 taken 6500 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6500 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6500 times.
19500 || !index->get_field(0)->is_ascending ||
2899
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6500 times.
6500 strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2900 return (FTS_INCORRECT_DOC_ID_INDEX);
2901 }
2902
2903 /* Check whether the index has FTS_DOC_ID as its
2904 first column */
2905 6500 field = index->get_field(0);
2906
2907 /* The column would be of a BIGINT data type */
2908 6500 if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0 &&
2909
2/4
✓ Branch 0 taken 6500 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6500 times.
✗ Branch 3 not taken.
6500 field->col->mtype == DATA_INT && field->col->len == 8 &&
2910
4/8
✓ Branch 0 taken 6500 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6500 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6500 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6500 times.
✗ Branch 7 not taken.
13000 field->col->prtype & DATA_NOT_NULL && !field->col->is_virtual()) {
2911
2/2
✓ Branch 0 taken 599 times.
✓ Branch 1 taken 5901 times.
6500 if (fts_doc_col_no) {
2912 599 *fts_doc_col_no = dict_col_get_no(field->col);
2913 }
2914 6500 return (FTS_EXIST_DOC_ID_INDEX);
2915 } else {
2916 return (FTS_INCORRECT_DOC_ID_INDEX);
2917 }
2918 }
2919
2920 /* Not found */
2921 137 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2922 }
2923 /** Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2924 on the Doc ID column in MySQL create index definition.
2925 @return FTS_EXIST_DOC_ID_INDEX if there exists the FTS_DOC_ID index,
2926 FTS_INCORRECT_DOC_ID_INDEX if the FTS_DOC_ID index is of wrong format */
2927 856 enum fts_doc_id_index_enum innobase_fts_check_doc_id_index_in_def(
2928 ulint n_key, /*!< in: Number of keys */
2929 const KEY *key_info) /*!< in: Key definition */
2930 {
2931 /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index
2932 list */
2933
2/2
✓ Branch 0 taken 1590 times.
✓ Branch 1 taken 848 times.
2438 for (ulint j = 0; j < n_key; j++) {
2934 1590 const KEY *key = &key_info[j];
2935
2936
2/2
✓ Branch 0 taken 1582 times.
✓ Branch 1 taken 8 times.
1590 if (innobase_strcasecmp(key->name, FTS_DOC_ID_INDEX_NAME)) {
2937 1582 continue;
2938 }
2939
2940 /* Do a check on FTS DOC ID_INDEX, it must be unique,
2941 named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */
2942
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (!(key->flags & HA_NOSAME) ||
2943
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 key->user_defined_key_parts != 1
2944 /* For now, we do not allow a descending index,
2945 because fts_doc_fetch_by_doc_id() uses the
2946 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2947
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 || (key->key_part[0].key_part_flag & HA_REVERSE_SORT) ||
2948
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 strcmp(key->name, FTS_DOC_ID_INDEX_NAME) ||
2949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 strcmp(key->key_part[0].field->field_name, FTS_DOC_ID_COL_NAME)) {
2950 return (FTS_INCORRECT_DOC_ID_INDEX);
2951 }
2952
2953 8 return (FTS_EXIST_DOC_ID_INDEX);
2954 }
2955
2956 848 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2957 }
2958
2959 /** Create an index table where indexes are ordered as follows:
2960
2961 IF a new primary key is defined for the table THEN
2962
2963 1) New primary key
2964 2) The remaining keys in key_info
2965
2966 ELSE
2967
2968 1) All new indexes in the order they arrive from MySQL
2969
2970 ENDIF
2971
2972 @return key definitions */
2973 template <typename Table>
2974 [[nodiscard]] static MY_ATTRIBUTE((malloc)) ddl::Index_defn
2975 71520 *innobase_create_key_defs(mem_heap_t *heap,
2976 /*!< in/out: memory heap where space for key
2977 definitions are allocated */
2978 const Alter_inplace_info *ha_alter_info,
2979 /*!< in: alter operation */
2980 const TABLE *altered_table,
2981 /*!< in: MySQL table that is being altered */
2982 const Table *new_dd_table,
2983 /*!< in: new dd table */
2984 ulint &n_add,
2985 /*!< in/out: number of indexes to be created */
2986 ulint &n_fts_add,
2987 /*!< out: number of FTS indexes to be created */
2988 bool got_default_clust,
2989 /*!< in: whether the table lacks a primary key */
2990 ulint &fts_doc_id_col,
2991 /*!< in: The column number for Doc ID */
2992 bool &add_fts_doc_id,
2993 /*!< in: whether we need to add new DOC ID
2994 column for FTS index */
2995 bool &add_fts_doc_idx,
2996 /*!< in: whether we need to add new DOC ID
2997 index for FTS index */
2998 const TABLE *table, bool is_file_per_table)
2999 /*!<in: old_table MySQL table as it is before the ALTER operation */
3000 {
3001 ddl::Index_defn *indexdef;
3002 ddl::Index_defn *index_defs;
3003 bool new_primary;
3004 71520 const uint *const add = ha_alter_info->index_add_buffer;
3005 71520 const KEY *const key_info = ha_alter_info->key_info_buffer;
3006
3007
1/2
✓ Branch 0 taken 35760 times.
✗ Branch 1 not taken.
71520 DBUG_TRACE;
3008
3/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 35638 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
71520 assert(!add_fts_doc_id || add_fts_doc_idx);
3009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 assert(ha_alter_info->index_add_count == n_add);
3010
3011 /* If there is a primary key, it is always the first index
3012 defined for the innodb_table. */
3013
3014
5/6
✓ Branch 0 taken 7544 times.
✓ Branch 1 taken 28216 times.
✓ Branch 2 taken 7544 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1201 times.
✓ Branch 5 taken 6343 times.
71520 new_primary = n_add > 0 && !my_strcasecmp(system_charset_info,
3015 key_info[*add].name, "PRIMARY");
3016 71520 n_fts_add = 0;
3017
3018 /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
3019 columns and if the index does not contain column prefix(es)
3020 (only prefix/part of the column is indexed), MySQL will treat the
3021 index as a PRIMARY KEY unless the table already has one. */
3022
3023
4/6
✓ Branch 0 taken 10611 times.
✓ Branch 1 taken 25149 times.
✓ Branch 2 taken 10611 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35760 times.
71520 ut_ad(altered_table->s->primary_key == 0 ||
3024 altered_table->s->primary_key == MAX_KEY);
3025
3026
4/4
✓ Branch 0 taken 11550 times.
✓ Branch 1 taken 24210 times.
✓ Branch 2 taken 11023 times.
✓ Branch 3 taken 527 times.
71520 if (got_default_clust && !new_primary) {
3027 22046 new_primary = (altered_table->s->primary_key != MAX_KEY);
3028 }
3029
3030 71520 const bool rebuild =
3031
6/6
✓ Branch 0 taken 34147 times.
✓ Branch 1 taken 1613 times.
✓ Branch 2 taken 34025 times.
✓ Branch 3 taken 122 times.
✓ Branch 4 taken 28732 times.
✓ Branch 5 taken 5293 times.
139570 new_primary || add_fts_doc_id ||
3032 68050 innobase_need_rebuild(ha_alter_info, table, is_file_per_table);
3033
3034 /* Reserve one more space if new_primary is true, and we might
3035 need to add the FTS_DOC_ID_INDEX */
3036 143040 indexdef = index_defs = static_cast<ddl::Index_defn *>(mem_heap_alloc(
3037 heap, sizeof *indexdef *
3038
1/2
✓ Branch 0 taken 35760 times.
✗ Branch 1 not taken.
71520 (ha_alter_info->key_count + rebuild + got_default_clust)));
3039
3040
2/2
✓ Branch 0 taken 30467 times.
✓ Branch 1 taken 5293 times.
71520 if (rebuild) {
3041 ulint primary_key_number;
3042
3043
2/2
✓ Branch 0 taken 1613 times.
✓ Branch 1 taken 28854 times.
60934 if (new_primary) {
3044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1613 times.
3226 if (n_add == 0) {
3045 assert(got_default_clust);
3046 assert(altered_table->s->primary_key == 0);
3047 primary_key_number = 0;
3048
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 1527 times.
3226 } else if (ha_alter_info->handler_flags &
3049 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) {
3050 172 primary_key_number = altered_table->s->primary_key;
3051 } else {
3052 3054 primary_key_number = *add;
3053 }
3054
2/2
✓ Branch 0 taken 6749 times.
✓ Branch 1 taken 22105 times.
57708 } else if (got_default_clust) {
3055 /* Create the GEN_CLUST_INDEX */
3056 13498 ddl::Index_defn *index_def = indexdef++;
3057
3058 13498 index_def->m_fields = nullptr;
3059 13498 index_def->m_n_fields = 0;
3060 13498 index_def->m_ind_type = DICT_CLUSTERED;
3061 13498 index_def->m_name = innobase_index_reserve_name;
3062 13498 index_def->m_rebuild = true;
3063 13498 index_def->m_key_number = std::numeric_limits<size_t>::max();
3064 13498 index_def->m_is_ngram = false;
3065 13498 primary_key_number = ULINT_UNDEFINED;
3066 13498 goto created_clustered;
3067 } else {
3068 44210 primary_key_number = 0;
3069 }
3070
3071 /* Create the PRIMARY key index definition */
3072
1/2
✓ Branch 0 taken 23718 times.
✗ Branch 1 not taken.
47436 innobase_create_index_def(altered_table, new_dd_table, key_info,
3073 primary_key_number, true, true, indexdef++, heap);
3074
3075 60934 created_clustered:
3076 60934 n_add = 1;
3077
3078
2/2
✓ Branch 0 taken 33184 times.
✓ Branch 1 taken 30467 times.
127302 for (ulint i = 0; i < ha_alter_info->key_count; i++) {
3079
2/2
✓ Branch 0 taken 23718 times.
✓ Branch 1 taken 9466 times.
66368 if (i == primary_key_number) {
3080 47436 continue;
3081 }
3082 /* Copy the index definitions. */
3083
1/2
✓ Branch 0 taken 9466 times.
✗ Branch 1 not taken.
18932 innobase_create_index_def(altered_table, new_dd_table, key_info, i, true,
3084 false, indexdef, heap);
3085
3086
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 9343 times.
18932 if (indexdef->m_ind_type & DICT_FTS) {
3087 246 n_fts_add++;
3088 }
3089
3090 18932 indexdef++;
3091 18932 n_add++;
3092 }
3093
3094
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 30344 times.
60934 if (n_fts_add > 0) {
3095 246 ulint num_v = 0;
3096
3097
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 122 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
248 if (!add_fts_doc_id &&
3098
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 !innobase_fts_check_doc_id_col(nullptr, altered_table,
3099 &fts_doc_id_col, &num_v)) {
3100 fts_doc_id_col = altered_table->s->fields - num_v;
3101 add_fts_doc_id = true;
3102 }
3103
3104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
246 if (!add_fts_doc_idx) {
3105 fts_doc_id_index_enum ret;
3106 ulint doc_col_no;
3107
3108 ret = innobase_fts_check_doc_id_index(nullptr, altered_table,
3109 &doc_col_no);
3110
3111 /* This should have been checked before */
3112 ut_ad(ret != FTS_INCORRECT_DOC_ID_INDEX);
3113
3114 if (ret == FTS_NOT_EXIST_DOC_ID_INDEX) {
3115 add_fts_doc_idx = true;
3116 } else {
3117 ut_ad(ret == FTS_EXIST_DOC_ID_INDEX);
3118 ut_ad(doc_col_no == ULINT_UNDEFINED || doc_col_no == fts_doc_id_col);
3119 }
3120 }
3121 }
3122 } else {
3123 /* Create definitions for added secondary indexes. */
3124
3125
2/2
✓ Branch 0 taken 5506 times.
✓ Branch 1 taken 5293 times.
21598 for (ulint i = 0; i < n_add; i++) {
3126
1/2
✓ Branch 0 taken 5506 times.
✗ Branch 1 not taken.
11012 innobase_create_index_def(altered_table, new_dd_table, key_info, add[i],
3127 false, false, indexdef, heap);
3128
3129
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 5160 times.
11012 if (indexdef->m_ind_type & DICT_FTS) {
3130 692 n_fts_add++;
3131 }
3132
3133 11012 indexdef++;
3134 }
3135 }
3136
3137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 assert(index_defs + n_add == indexdef);
3138
3139
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 35623 times.
71520 if (add_fts_doc_idx) {
3140 274 ddl::Index_defn *index_def = indexdef++;
3141
3142 274 index_def->m_fields = static_cast<ddl::Index_field *>(
3143
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
274 mem_heap_alloc(heap, sizeof *index_def->m_fields));
3144
3145 274 index_def->m_n_fields = 1;
3146 274 index_def->m_fields->m_col_no = fts_doc_id_col;
3147 274 index_def->m_fields->m_prefix_len = 0;
3148 274 index_def->m_fields->m_is_ascending = true;
3149 274 index_def->m_fields->m_is_v_col = false;
3150 274 index_def->m_ind_type = DICT_UNIQUE;
3151
6/8
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 122 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 137 times.
274 ut_ad(!rebuild || !add_fts_doc_id ||
3152 fts_doc_id_col <= altered_table->s->fields);
3153
3154 274 index_def->m_name = FTS_DOC_ID_INDEX_NAME;
3155 274 index_def->m_is_ngram = false;
3156 274 index_def->m_rebuild = rebuild;
3157
3158 /* TODO: assign a real MySQL key number for this */
3159 274 index_def->m_key_number = ULINT_UNDEFINED;
3160 274 n_add++;
3161 }
3162
3163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 assert(indexdef > index_defs);
3164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 assert((ulint)(indexdef - index_defs) <=
3165 ha_alter_info->key_count + add_fts_doc_idx + got_default_clust);
3166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 assert(ha_alter_info->index_add_count <= n_add);
3167 71520 return index_defs;
3168 71520 }
3169
3170 /** Check each index column size, make sure they do not exceed the max limit
3171 @return true if index column size exceeds limit */
3172 7341 [[nodiscard]] static bool innobase_check_column_length(
3173 ulint max_col_len, /*!< in: maximum column length */
3174 const KEY *key_info) /*!< in: Indexes to be created */
3175 {
3176
2/2
✓ Branch 0 taken 11145 times.
✓ Branch 1 taken 7337 times.
18482 for (ulint key_part = 0; key_part < key_info->user_defined_key_parts;
3177 key_part++) {
3178
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 11141 times.
11145 if (key_info->key_part[key_part].length > max_col_len) {
3179 4 return (true);
3180 }
3181 }
3182 7337 return (false);
3183 }
3184
3185 /** Search for a given column in each index that is not being dropped. Return
3186 true if the column is part of any of the active indexes or it is a system
3187 column.
3188 @param[in] table table object
3189 @param[in] col_no column number of the column which is to be checked
3190 @param[in] is_v if this is a virtual column
3191 @retval true the column exists or it is a system column
3192 @retval false column does not exist */
3193 3704 static bool check_col_exists_in_indexes(const dict_table_t *table, ulint col_no,
3194 bool is_v) {
3195 /* This function does not check system columns */
3196
6/6
✓ Branch 0 taken 3552 times.
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 1194 times.
✓ Branch 3 taken 2358 times.
✓ Branch 4 taken 1194 times.
✓ Branch 5 taken 2510 times.
3704 if (!is_v && table->get_col(col_no)->mtype == DATA_SYS) {
3197 1194 return (true);
3198 }
3199
3200
2/2
✓ Branch 0 taken 3884 times.
✓ Branch 1 taken 1959 times.
5843 for (const dict_index_t *index = table->first_index(); index;
3201 3333 index = index->next()) {
3202
2/2
✓ Branch 0 taken 2971 times.
✓ Branch 1 taken 3333 times.
6304 for (ulint i = 0; i < index->n_user_defined_cols; i++) {
3203 2971 const dict_col_t *idx_col = index->get_col(i);
3204
3205
6/6
✓ Branch 0 taken 230 times.
✓ Branch 1 taken 2741 times.
✓ Branch 2 taken 138 times.
✓ Branch 3 taken 92 times.
✓ Branch 4 taken 138 times.
✓ Branch 5 taken 2833 times.
2971 if (is_v && idx_col->is_virtual()) {
3206 138 const dict_v_col_t *v_col =
3207 reinterpret_cast<const dict_v_col_t *>(idx_col);
3208
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 113 times.
138 if (v_col->v_pos == col_no) {
3209 25 return (true);
3210 }
3211 }
3212
3213
6/6
✓ Branch 0 taken 2741 times.
✓ Branch 1 taken 205 times.
✓ Branch 2 taken 2688 times.
✓ Branch 3 taken 53 times.
✓ Branch 4 taken 526 times.
✓ Branch 5 taken 2420 times.
5634 if (!is_v && !idx_col->is_virtual() &&
3214
2/2
✓ Branch 0 taken 526 times.
✓ Branch 1 taken 2162 times.
2688 dict_col_get_no(idx_col) == col_no) {
3215 526 return (true);
3216 }
3217 }
3218 }
3219
3220 1959 return (false);
3221 }
3222
3223 /** Reset dict_col_t::ord_part for those columns that fail to be indexed,
3224 Check every existing column to see if any current index references them.
3225 This should be checked after an index is dropped during ALTER TABLE.
3226 @param[in,out] table InnoDB table to check */
3227 398 static inline void reset_column_ord_part(dict_table_t *table) {
3228
2/2
✓ Branch 0 taken 2383 times.
✓ Branch 1 taken 398 times.
2781 for (ulint i = 0; i < table->get_n_cols(); i++) {
3229
2/2
✓ Branch 0 taken 915 times.
✓ Branch 1 taken 1468 times.
2383 if (!check_col_exists_in_indexes(table, i, false)) {
3230 915 table->cols[i].ord_part = 0;
3231 }
3232 }
3233
3234
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 398 times.
439 for (ulint i = 0; i < dict_table_get_n_v_cols(table); i++) {
3235
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 16 times.
41 if (!check_col_exists_in_indexes(table, i, true)) {
3236 25 table->v_cols[i].m_col.ord_part = 0;
3237 }
3238 }
3239 398 }
3240
3241 /** Drop in-memory metadata for index (dict_index_t) left from previous
3242 online ALTER operation.
3243 @param[in] table table to check
3244 @param[in] locked if it is dict_sys mutex locked */
3245 47886 static void online_retry_drop_dict_indexes(dict_table_t *table, bool locked) {
3246
2/2
✓ Branch 0 taken 12128 times.
✓ Branch 1 taken 35758 times.
47886 if (!locked) {
3247 12128 dict_sys_mutex_enter();
3248 }
3249
3250 47886 bool modify = false;
3251 47886 dict_index_t *index = table->first_index();
3252
3253
2/2
✓ Branch 0 taken 16927 times.
✓ Branch 1 taken 47886 times.
64813 for (index = index->next(); index != nullptr; index = index->next()) {
3254
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 16922 times.
16927 if (dict_index_get_online_status(index) == ONLINE_INDEX_ABORTED_DROPPED) {
3255 5 dict_index_t *prev = UT_LIST_GET_PREV(indexes, index);
3256
3257 5 dict_index_remove_from_cache(table, index);
3258
3259 5 index = prev;
3260
3261 5 modify = true;
3262 }
3263 }
3264
3265
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 47881 times.
47886 if (modify) {
3266 /* Since the table has been modified, table->def_trx_id should be
3267 adjusted like ddl::drop_indexes(). However, this function may
3268 be called before the DDL transaction starts, so it is impossible to
3269 get current DDL transaction ID. Thus advancing def_trx_id by 1 to
3270 simply inform other threads about this change. */
3271 5 ++table->def_trx_id;
3272
3273 5 reset_column_ord_part(table);
3274 }
3275
3276
2/2
✓ Branch 0 taken 12128 times.
✓ Branch 1 taken 35758 times.
47886 if (!locked) {
3277 12128 dict_sys_mutex_exit();
3278 }
3279 47886 }
3280
3281 /** Determines if InnoDB is dropping a foreign key constraint.
3282 @param foreign the constraint
3283 @param drop_fk constraints being dropped
3284 @param n_drop_fk number of constraints that are being dropped
3285 @return whether the constraint is being dropped */
3286 14 [[nodiscard]] inline bool innobase_dropping_foreign(
3287 const dict_foreign_t *foreign, dict_foreign_t **drop_fk, ulint n_drop_fk) {
3288
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 while (n_drop_fk--) {
3289
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (*drop_fk++ == foreign) {
3290 2 return (true);
3291 }
3292 }
3293
3294 12 return (false);
3295 }
3296
3297 /** Convert a default value for ADD COLUMN.
3298
3299 @param heap Memory heap where allocated
3300 @param dfield InnoDB data field to copy to
3301 @param field MySQL value for the column
3302 @param comp nonzero if in compact format */
3303 17730 static void innobase_build_col_map_add(mem_heap_t *heap, dfield_t *dfield,
3304 const Field *field, ulint comp,
3305 row_prebuilt_t *prebuilt) {
3306
2/2
✓ Branch 0 taken 13625 times.
✓ Branch 1 taken 4105 times.
17730 if (field->is_real_null()) {
3307 13625 dfield_set_null(dfield);
3308 13625 return;
3309 }
3310
3311 4105 ulint size = field->pack_length();
3312
3313 4105 byte *buf = static_cast<byte *>(mem_heap_alloc(heap, size));
3314
3315 4105 const byte *mysql_data = field->field_ptr();
3316
3317 4105 row_mysql_store_col_in_innobase_format(
3318 dfield, buf, true, mysql_data, size, comp,
3319 4105 field->column_format() == COLUMN_FORMAT_TYPE_COMPRESSED,
3320 4105 reinterpret_cast<const byte *>(field->zip_dict_data.str),
3321 4105 field->zip_dict_data.length, &prebuilt->compress_heap);
3322 }
3323
3324 /** Construct the translation table for reordering, dropping or
3325 adding columns.
3326
3327 @param ha_alter_info Data used during in-place alter
3328 @param altered_table MySQL table that is being altered
3329 @param table MySQL table as it is before the ALTER operation
3330 @param new_table InnoDB table corresponding to MySQL altered_table
3331 @param old_table InnoDB table corresponding to MYSQL table
3332 @param add_cols Default values for ADD COLUMN, or NULL if no ADD COLUMN
3333 @param heap Memory heap where allocated
3334 @return array of integers, mapping column numbers in the table
3335 to column numbers in altered_table */
3336 30438 [[nodiscard]] static const ulint *innobase_build_col_map(
3337 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
3338 const TABLE *table, const dict_table_t *new_table,
3339 const dict_table_t *old_table, dtuple_t *add_cols, mem_heap_t *heap,
3340 row_prebuilt_t *prebuilt) {
3341
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
30438 DBUG_TRACE;
3342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
30438 assert(altered_table != table);
3343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
30438 assert(new_table != old_table);
3344
3/6
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30438 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30438 times.
30438 assert(new_table->get_n_cols() + dict_table_get_n_v_cols(new_table) >=
3345 altered_table->s->fields + DATA_N_SYS_COLS);
3346
3/6
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30438 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30438 times.
30438 assert(old_table->get_n_cols() + dict_table_get_n_v_cols(old_table) >=
3347 table->s->fields + DATA_N_SYS_COLS);
3348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
30438 assert(!!add_cols ==
3349 !!(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN));
3350
5/8
✓ Branch 0 taken 9342 times.
✓ Branch 1 taken 21096 times.
✓ Branch 2 taken 9342 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9342 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 9342 times.
30438 assert(!add_cols || dtuple_get_n_fields(add_cols) == new_table->get_n_cols());
3351
3352 60876 ulint *col_map = static_cast<ulint *>(mem_heap_alloc(
3353
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
30438 heap, (old_table->n_cols + old_table->n_v_cols) * sizeof *col_map));
3354
3355 List_iterator_fast<Create_field> cf_it(
3356
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
30438 ha_alter_info->alter_info->create_list);
3357 30438 uint i = 0;
3358 30438 uint num_v = 0;
3359
3360 /* Any dropped columns will map to ULINT_UNDEFINED. */
3361
2/2
✓ Branch 0 taken 208901 times.
✓ Branch 1 taken 30438 times.
239339 for (uint old_i = 0; old_i + DATA_N_SYS_COLS < old_table->n_cols; old_i++) {
3362 208901 col_map[old_i] = ULINT_UNDEFINED;
3363 }
3364
3365
2/2
✓ Branch 0 taken 837 times.
✓ Branch 1 taken 30438 times.
31275 for (uint old_i = 0; old_i < old_table->n_v_cols; old_i++) {
3366 837 col_map[old_i + old_table->n_cols] = ULINT_UNDEFINED;
3367 }
3368
3369
2/2
✓ Branch 0 taken 225961 times.
✓ Branch 1 taken 30438 times.
256399 while (const Create_field *new_field = cf_it++) {
3370 225961 bool is_v = false;
3371
3372
4/4
✓ Branch 0 taken 859 times.
✓ Branch 1 taken 225102 times.
✓ Branch 2 taken 837 times.
✓ Branch 3 taken 22 times.
225961 if (innobase_is_v_fld(new_field)) {
3373 837 is_v = true;
3374 }
3375
3376 225961 ulint num_old_v = 0;
3377
3378
2/2
✓ Branch 0 taken 3048065 times.
✓ Branch 1 taken 17730 times.
3065795 for (uint old_i = 0; table->field[old_i]; old_i++) {
3379 3048065 const Field *field = table->field[old_i];
3380
4/4
✓ Branch 0 taken 950 times.
✓ Branch 1 taken 3047115 times.
✓ Branch 2 taken 890 times.
✓ Branch 3 taken 60 times.
3048065 if (innobase_is_v_fld(field)) {
3381
4/4
✓ Branch 0 taken 856 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 837 times.
✓ Branch 3 taken 19 times.
890 if (is_v && new_field->field == field) {
3382 837 col_map[old_table->n_cols + num_v] = num_old_v;
3383 837 num_old_v++;
3384 837 goto found_col;
3385 }
3386 53 num_old_v++;
3387 53 continue;
3388 }
3389
3390
2/2
✓ Branch 0 taken 207394 times.
✓ Branch 1 taken 2839781 times.
3047175 if (new_field->field == field) {
3391 207394 col_map[old_i - num_old_v] = i;
3392 207394 goto found_col;
3393 }
3394 }
3395
3396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17730 times.
17730 ut_ad(!is_v);
3397
1/2
✓ Branch 0 taken 17730 times.
✗ Branch 1 not taken.
17730 innobase_build_col_map_add(heap, dtuple_get_nth_field(add_cols, i),
3398 17730 altered_table->field[i + num_v],
3399
2/4
✓ Branch 0 taken 17730 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17730 times.
✗ Branch 3 not taken.
17730 dict_table_is_comp(new_table), prebuilt);
3400 225961 found_col:
3401
2/2
✓ Branch 0 taken 837 times.
✓ Branch 1 taken 225124 times.
225961 if (is_v) {
3402 837 num_v++;
3403 } else {
3404 225124 i++;
3405 }
3406 225961 }
3407
3408
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
30438 assert(i == altered_table->s->fields - num_v);
3409
3410 30438 i = table->s->fields - old_table->n_v_cols;
3411
3412 /* Add the InnoDB hidden FTS_DOC_ID column, if any. */
3413
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 30403 times.
30438 if (i + DATA_N_SYS_COLS < old_table->n_cols) {
3414 /* There should be exactly one extra field,
3415 the FTS_DOC_ID. */
3416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 assert(DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID));
3417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 assert(i + DATA_N_SYS_COLS + 1 == old_table->n_cols);
3418
2/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35 times.
35 assert(!strcmp(old_table->get_col_name(i), FTS_DOC_ID_COL_NAME));
3419 35 if (altered_table->s->fields + DATA_N_SYS_COLS - new_table->n_v_cols <
3420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 new_table->n_cols) {
3421 assert(DICT_TF2_FLAG_IS_SET(new_table, DICT_TF2_FTS_HAS_DOC_ID));
3422 assert(altered_table->s->fields + DATA_N_SYS_COLS + 1 ==
3423 static_cast<ulint>(new_table->n_cols + new_table->n_v_cols));
3424 col_map[i] = altered_table->s->fields - new_table->n_v_cols;
3425 } else {
3426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 assert(!DICT_TF2_FLAG_IS_SET(new_table, DICT_TF2_FTS_HAS_DOC_ID));
3427 35 col_map[i] = ULINT_UNDEFINED;
3428 }
3429
3430 35 i++;
3431 } else {
3432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30403 times.
30403 assert(!DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID));
3433 }
3434
3435
2/2
✓ Branch 0 taken 91314 times.
✓ Branch 1 taken 30438 times.
121752 for (; i < old_table->n_cols; i++) {
3436 91314 col_map[i] = i + new_table->n_cols - old_table->n_cols;
3437 }
3438
3439 30438 return col_map;
3440 30438 }
3441
3442 /** Drop newly create FTS index related auxiliary table during
3443 FIC create index process, before fts_add_index is called
3444 @param table table that was being rebuilt online
3445 @param trx transaction
3446 @return DB_SUCCESS if successful, otherwise last error code
3447 */
3448 4 static dberr_t innobase_drop_fts_index_table(dict_table_t *table, trx_t *trx) {
3449 4 dberr_t ret_err = DB_SUCCESS;
3450
3451
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 for (dict_index_t *index = table->first_index(); index != nullptr;
3452 12 index = index->next()) {
3453
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 if (index->type & DICT_FTS) {
3454 dberr_t err;
3455
3456 4 err = fts_drop_index_tables(trx, index, nullptr);
3457
3458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (err != DB_SUCCESS) {
3459 ret_err = err;
3460 }
3461 }
3462 }
3463
3464 4 return (ret_err);
3465 }
3466
3467 /** Get the new non-virtual column names if any columns were renamed
3468 @param ha_alter_info Data used during in-place alter
3469 @param altered_table MySQL table that is being altered
3470 @param table MySQL table as it is before the ALTER operation
3471 @param user_table InnoDB table as it is before the ALTER operation
3472 @param heap Memory heap for the allocation
3473 @return array of new column names in rebuilt_table, or NULL if not renamed */
3474 64 [[nodiscard]] static const char **innobase_get_col_names(
3475 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
3476 const TABLE *table, const dict_table_t *user_table, mem_heap_t *heap) {
3477 const char **cols;
3478 uint i;
3479
3480
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 DBUG_TRACE;
3481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(user_table->n_t_def > table->s->fields);
3482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME);
3483
3484 cols = static_cast<const char **>(
3485
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 mem_heap_zalloc(heap, user_table->n_def * sizeof *cols));
3486
3487 64 i = 0;
3488 List_iterator_fast<Create_field> cf_it(
3489
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 ha_alter_info->alter_info->create_list);
3490
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 64 times.
336 while (const Create_field *new_field = cf_it++) {
3491 272 ulint num_v = 0;
3492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 272 times.
272 assert(i < altered_table->s->fields);
3493
3494
3/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 228 times.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
272 if (innobase_is_v_fld(new_field)) {
3495 44 continue;
3496 }
3497
3498
2/2
✓ Branch 0 taken 706 times.
✓ Branch 1 taken 7 times.
713 for (uint old_i = 0; table->field[old_i]; old_i++) {
3499
3/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 672 times.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
706 if (innobase_is_v_fld(table->field[old_i])) {
3500 34 num_v++;
3501 }
3502
3503
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 485 times.
706 if (new_field->field == table->field[old_i]) {
3504 221 cols[old_i - num_v] = new_field->field_name;
3505 221 break;
3506 }
3507 }
3508
3509 228 i++;
3510 272 }
3511
3512 /* Copy the internal column names. */
3513 64 i = table->s->fields - user_table->n_v_def;
3514
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 cols[i] = user_table->get_col_name(i);
3515
3516
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 64 times.
196 while (++i < user_table->n_def) {
3517 132 cols[i] = cols[i - 1] + strlen(cols[i - 1]) + 1;
3518 }
3519
3520 64 return cols;
3521 64 }
3522
3523 /** Check whether the column prefix is increased, decreased, or unchanged.
3524 @param[in] new_prefix_len new prefix length
3525 @param[in] old_prefix_len new prefix length
3526 @retval 1 prefix is increased
3527 @retval 0 prefix is unchanged
3528 @retval -1 prefix is decreased */
3529 39865 static inline lint innobase_pk_col_prefix_compare(ulint new_prefix_len,
3530 ulint old_prefix_len) {
3531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39865 times.
39865 ut_ad(new_prefix_len < REC_MAX_DATA_SIZE);
3532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39865 times.
39865 ut_ad(old_prefix_len < REC_MAX_DATA_SIZE);
3533
3534
2/2
✓ Branch 0 taken 39798 times.
✓ Branch 1 taken 67 times.
39865 if (new_prefix_len == old_prefix_len) {
3535 39798 return (0);
3536 }
3537
3538
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 52 times.
67 if (new_prefix_len == 0) {
3539 15 new_prefix_len = ULINT_MAX;
3540 }
3541
3542
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 51 times.
67 if (old_prefix_len == 0) {
3543 16 old_prefix_len = ULINT_MAX;
3544 }
3545
3546
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 36 times.
67 if (new_prefix_len > old_prefix_len) {
3547 31 return (1);
3548 } else {
3549 36 return (-1);
3550 }
3551 }
3552
3553 /** Check whether the column is existing in old table.
3554 @param[in] new_col_no new column no
3555 @param[in] col_map mapping of old column numbers to new ones
3556 @param[in] col_map_size the column map size
3557 @return true if the column is existing, otherwise false. */
3558 120 static inline bool innobase_pk_col_is_existing(const ulint new_col_no,
3559 const ulint *col_map,
3560 const ulint col_map_size) {
3561
2/2
✓ Branch 0 taken 549 times.
✓ Branch 1 taken 64 times.
613 for (ulint i = 0; i < col_map_size; i++) {
3562
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 493 times.
549 if (col_map[i] == new_col_no) {
3563 56 return (true);
3564 }
3565 }
3566
3567 64 return (false);
3568 }
3569
3570 /** Determine whether both the indexes have same set of primary key
3571 fields arranged in the same order.
3572
3573 Rules when we cannot skip sorting:
3574 (1) Removing existing PK columns somewhere else than at the end of the PK;
3575 (2) Adding existing columns to the PK, except at the end of the PK when no
3576 columns are removed from the PK;
3577 (3) Changing the order of existing PK columns;
3578 (4) Decreasing the prefix length just like removing existing PK columns
3579 follows rule(1), Increasing the prefix length just like adding existing
3580 PK columns follows rule(2);
3581 (5) Changing the ascending order of the existing PK columns.
3582 @param[in] col_map mapping of old column numbers to new ones
3583 @param[in] old_clust_index index to be compared
3584 @param[in] new_clust_index index to be compared
3585 @retval true if both indexes have same order.
3586 @retval false. */
3587 30364 [[nodiscard]] static bool innobase_pk_order_preserved(
3588 const ulint *col_map, const dict_index_t *old_clust_index,
3589 const dict_index_t *new_clust_index) {
3590 30364 ulint old_n_uniq = dict_index_get_n_ordering_defined_by_user(old_clust_index);
3591 30364 ulint new_n_uniq = dict_index_get_n_ordering_defined_by_user(new_clust_index);
3592
3593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30364 times.
30364 ut_ad(old_clust_index->is_clustered());
3594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30364 times.
30364 ut_ad(new_clust_index->is_clustered());
3595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30364 times.
30364 ut_ad(old_clust_index->table != new_clust_index->table);
3596
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30364 times.
30364 ut_ad(col_map != nullptr);
3597
3598
2/2
✓ Branch 0 taken 7683 times.
✓ Branch 1 taken 22681 times.
30364 if (old_n_uniq == 0) {
3599 /* There was no PRIMARY KEY in the table.
3600 If there is no PRIMARY KEY after the ALTER either,
3601 no sorting is needed. */
3602 7683 return (new_n_uniq == old_n_uniq);
3603 }
3604
3605 /* DROP PRIMARY KEY is only allowed in combination with
3606 ADD PRIMARY KEY. */
3607
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22681 times.
22681 ut_ad(new_n_uniq > 0);
3608
3609 /* The order of the last processed new_clust_index key field,
3610 not counting ADD COLUMN, which are constant. */
3611 22681 lint last_field_order = -1;
3612 22681 ulint existing_field_count = 0;
3613 22681 ulint old_n_cols = old_clust_index->table->get_n_cols();
3614
2/2
✓ Branch 0 taken 40504 times.
✓ Branch 1 taken 22123 times.
62627 for (ulint new_field = 0; new_field < new_n_uniq; new_field++) {
3615 40504 ulint new_col_no = new_clust_index->fields[new_field].col->ind;
3616
3617 /* Check if there is a match in old primary key. */
3618 40504 ulint old_field = 0;
3619
2/2
✓ Branch 0 taken 72141 times.
✓ Branch 1 taken 120 times.
72261 while (old_field < old_n_uniq) {
3620 72141 ulint old_col_no = old_clust_index->fields[old_field].col->ind;
3621
3622
2/2
✓ Branch 0 taken 40384 times.
✓ Branch 1 taken 31757 times.
72141 if (col_map[old_col_no] == new_col_no) {
3623 40384 break;
3624 }
3625
3626 31757 old_field++;
3627 }
3628
3629 /* The order of key field in the new primary key.
3630 1. old PK column: idx in old primary key
3631 2. existing column: old_n_uniq + sequence no
3632 3. newly added column: no order */
3633 lint new_field_order;
3634 40504 const bool old_pk_column = old_field < old_n_uniq;
3635
3636
2/2
✓ Branch 0 taken 40384 times.
✓ Branch 1 taken 120 times.
40504 if (old_pk_column) {
3637 40384 new_field_order = old_field;
3638
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 64 times.
120 } else if (innobase_pk_col_is_existing(new_col_no, col_map, old_n_cols)) {
3639 56 new_field_order = old_n_uniq + existing_field_count++;
3640 } else {
3641 /* Skip newly added column. */
3642 64 continue;
3643 }
3644
3645
2/2
✓ Branch 0 taken 549 times.
✓ Branch 1 taken 39891 times.
40440 if (last_field_order + 1 != new_field_order) {
3646 /* Old PK order is not kept, or existing column
3647 is not added at the end of old PK. */
3648 549 return (false);
3649 }
3650
3651 39891 last_field_order = new_field_order;
3652
3653
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 39865 times.
39891 if (!old_pk_column) {
3654 26 continue;
3655 }
3656
3657 /* Check prefix length change. */
3658 79730 const lint prefix_change = innobase_pk_col_prefix_compare(
3659 39865 new_clust_index->fields[new_field].prefix_len,
3660 39865 old_clust_index->fields[old_field].prefix_len);
3661
3662
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 39829 times.
39865 if (prefix_change < 0) {
3663 /* If a column's prefix length is decreased, it should
3664 be the last old PK column in new PK.
3665 Note: we set last_field_order to -2, so that if there
3666 are any old PK colmns or existing columns after it in
3667 new PK, the comparison to new_field_order will fail in
3668 the next round.*/
3669 36 last_field_order = -2;
3670
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 39798 times.
39829 } else if (prefix_change > 0) {
3671 /* If a column's prefix length is increased, it should
3672 be the last PK column in old PK. */
3673
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
31 if (old_field != old_n_uniq - 1) {
3674 7 return (false);
3675 }
3676 }
3677
3678 /* Check new primary key field ascending or descending changes
3679 compared to old primary key field. */
3680 39858 bool change_asc = (new_clust_index->fields[new_field].is_ascending ==
3681 39858 old_clust_index->fields[old_field].is_ascending);
3682
3683
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 39856 times.
39858 if (!change_asc) {
3684 2 return (false);
3685 }
3686 }
3687
3688 22123 return (true);
3689 }
3690
3691 /** Check if we are creating spatial indexes on GIS columns, which are
3692 legacy columns from earlier MySQL, such as 5.6. If so, we have to update
3693 the mtypes of the old GIS columns to DATA_GEOMETRY.
3694 In 5.6, we store GIS columns as DATA_BLOB in InnoDB layer, it will introduce
3695 confusion when we run latest server on older data. That's why we need to
3696 do the upgrade.
3697 @param[in] ha_alter_info Data used during in-place alter
3698 @param[in] table Table on which we want to add indexes
3699 @return DB_SUCCESS if update successfully or no columns need to be updated,
3700 otherwise DB_ERROR, which means we can't update the mtype for some
3701 column, and creating spatial index on it should be dangerous */
3702 5286 static dberr_t innobase_check_gis_columns(Alter_inplace_info *ha_alter_info,
3703 dict_table_t *table) {
3704
1/2
✓ Branch 0 taken 5286 times.
✗ Branch 1 not taken.
5286 DBUG_TRACE;
3705
3706
2/2
✓ Branch 0 taken 5497 times.
✓ Branch 1 taken 5286 times.
10783 for (uint key_num = 0; key_num < ha_alter_info->index_add_count; key_num++) {
3707 5497 const KEY &key =
3708 ha_alter_info
3709 5497 ->key_info_buffer[ha_alter_info->index_add_buffer[key_num]];
3710
3711
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 77 times.
5497 if (!(key.flags & HA_SPATIAL)) {
3712 5497 continue;
3713 }
3714
3715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 ut_ad(key.user_defined_key_parts == 1);
3716 77 const KEY_PART_INFO &key_part = key.key_part[0];
3717
3718 /* Does not support spatial index on virtual columns */
3719
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
77 if (innobase_is_v_fld(key_part.field)) {
3720 return DB_UNSUPPORTED;
3721 }
3722
3723 154 ulint col_nr = dict_table_has_column(table, key_part.field->field_name,
3724
1/2
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
77 key_part.fieldnr);
3725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 ut_ad(col_nr != table->n_def);
3726 77 dict_col_t *col = &table->cols[col_nr];
3727
3728
1/2
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
77 if (col->mtype != DATA_BLOB) {
3729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 ut_ad(DATA_GEOMETRY_MTYPE(col->mtype));
3730 77 continue;
3731 }
3732
3733 const char *col_name = table->get_col_name(col_nr);
3734 col->mtype = DATA_GEOMETRY;
3735
3736 ib::info(ER_IB_MSG_598)
3737 << "Updated mtype of column" << col_name << " in table " << table->name
3738 << ", whose id is " << table->id << " to DATA_GEOMETRY";
3739 }
3740
3741 5286 return DB_SUCCESS;
3742 5286 }
3743
3744 /** Update the attributes for the implicit tablespaces
3745 @param[in] thd THD object
3746 @param[in] ha_alter_info Data used during in-place alter
3747 @param[in] table MySQL table that is being modified
3748 @return true Failure
3749 @return false Success */
3750 5034 static bool prepare_inplace_change_implicit_tablespace_option(
3751 THD *thd, Alter_inplace_info *ha_alter_info, const dict_table_t *table) {
3752
1/2
✓ Branch 0 taken 5034 times.
✗ Branch 1 not taken.
5034 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
3753
1/2
✓ Branch 0 taken 5034 times.
✗ Branch 1 not taken.
5034 dd::cache::Dictionary_client::Auto_releaser releaser(client);
3754
3755 5034 dd::Object_id space_id = table->dd_space_id;
3756
3757
1/2
✓ Branch 0 taken 5034 times.
✗ Branch 1 not taken.
5034 return dd_implicit_alter_tablespace(client, space_id,
3758 10068 ha_alter_info->create_info);
3759 5034 }
3760
3761 /** Collect virtual column info for its addition
3762 @param[in] ha_alter_info Data used during in-place alter
3763 @param[in] altered_table MySQL table that is being altered to
3764 @param[in] table MySQL table as it is before the ALTER operation
3765 @retval true Failure
3766 @retval false Success */
3767 317 static bool prepare_inplace_add_virtual(Alter_inplace_info *ha_alter_info,
3768 const TABLE *altered_table,
3769 const TABLE *table) {
3770 ha_innobase_inplace_ctx *ctx;
3771 317 ulint i = 0;
3772 317 ulint j = 0;
3773 const Create_field *new_field;
3774
3775 317 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
3776
3777 634 ctx->add_vcol = static_cast<dict_v_col_t *>(mem_heap_zalloc(
3778 ctx->heap,
3779
1/2
✓ Branch 0 taken 317 times.
✗ Branch 1 not taken.
317 ha_alter_info->virtual_column_add_count * sizeof *ctx->add_vcol));
3780 634 ctx->add_vcol_name = static_cast<const char **>(mem_heap_alloc(
3781 ctx->heap,
3782
1/2
✓ Branch 0 taken 317 times.
✗ Branch 1 not taken.
317 ha_alter_info->virtual_column_add_count * sizeof *ctx->add_vcol_name));
3783
3784 List_iterator_fast<Create_field> cf_it(
3785
1/2
✓ Branch 0 taken 317 times.
✗ Branch 1 not taken.
317 ha_alter_info->alter_info->create_list);
3786
3787
2/2
✓ Branch 0 taken 1578 times.
✓ Branch 1 taken 316 times.
1894 while ((new_field = (cf_it++)) != nullptr) {
3788 1578 const Field *field = new_field->field;
3789 ulint old_i;
3790
3791
2/2
✓ Branch 0 taken 4828 times.
✓ Branch 1 taken 330 times.
5158 for (old_i = 0; table->field[old_i]; old_i++) {
3792 4828 const Field *n_field = table->field[old_i];
3793
2/2
✓ Branch 0 taken 1248 times.
✓ Branch 1 taken 3580 times.
4828 if (field == n_field) {
3794 1248 break;
3795 }
3796 }
3797
3798 1578 i++;
3799
3800
2/2
✓ Branch 0 taken 1248 times.
✓ Branch 1 taken 330 times.
1578 if (table->field[old_i]) {
3801 1248 continue;
3802 }
3803
3804
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 ut_ad(!field);
3805
3806 ulint col_len;
3807 ulint is_unsigned;
3808 ulint field_type;
3809 ulint charset_no;
3810
3811 330 field = altered_table->field[i - 1];
3812
3813
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
330 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
3814
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
330 bool is_multi_value = innobase_is_multi_value_fld(field);
3815
3816
2/4
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 330 times.
330 if (!field->gcol_info || field->stored_in_db) {
3817 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3818 1 return (true);
3819 }
3820
3821
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 234 times.
330 if (is_multi_value) {
3822
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 col_len = field->key_length();
3823 } else {
3824
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 col_len = field->pack_length();
3825 }
3826
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
330 field_type = (ulint)field->type();
3827
3828
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 329 times.
330 if (!field->is_nullable()) {
3829 1 field_type |= DATA_NOT_NULL;
3830 }
3831
3832
3/4
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 304 times.
✓ Branch 3 taken 26 times.
330 if (field->binary()) {
3833 304 field_type |= DATA_BINARY_TYPE;
3834 }
3835
3836
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 304 times.
330 if (is_unsigned) {
3837 26 field_type |= DATA_UNSIGNED;
3838 }
3839
3840
3/4
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 260 times.
330 if (dtype_is_string_type(col_type)) {
3841
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 charset_no = (ulint)field->charset()->number;
3842
3843
3/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 69 times.
70 DBUG_EXECUTE_IF("ib_alter_add_virtual_fail",
3844 charset_no += MAX_CHAR_COLL_NUM;);
3845
3846
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 69 times.
70 if (charset_no > MAX_CHAR_COLL_NUM) {
3847
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3848 1 return (true);
3849 }
3850 } else {
3851 260 charset_no = 0;
3852 }
3853
3854
7/8
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 300 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 8 times.
✓ Branch 7 taken 321 times.
329 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
3855
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 uint32_t length_bytes = field->get_length_bytes();
3856
3857 8 col_len -= length_bytes;
3858
3859
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (length_bytes == 2) {
3860 2 field_type |= DATA_LONG_TRUE_VARCHAR;
3861 }
3862 }
3863
3864
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 ctx->add_vcol[j].m_col.prtype = dtype_form_prtype(field_type, charset_no);
3865
3866 329 ctx->add_vcol[j].m_col.prtype |= DATA_VIRTUAL;
3867
3868
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 233 times.
329 if (is_multi_value) {
3869 96 ctx->add_vcol[j].m_col.prtype |= DATA_MULTI_VALUE;
3870 }
3871
3872 329 ctx->add_vcol[j].m_col.mtype = col_type;
3873
3874 329 ctx->add_vcol[j].m_col.len = col_len;
3875
3876 329 ctx->add_vcol[j].m_col.ind = i - 1;
3877 329 ctx->add_vcol[j].num_base = field->gcol_info->non_virtual_base_columns();
3878 329 ctx->add_vcol_name[j] = field->field_name;
3879 658 ctx->add_vcol[j].base_col = static_cast<dict_col_t **>(mem_heap_alloc(
3880 ctx->heap,
3881
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 ctx->add_vcol[j].num_base * sizeof *(ctx->add_vcol[j].base_col)));
3882 329 ctx->add_vcol[j].v_pos =
3883 329 ctx->old_table->n_v_cols - ha_alter_info->virtual_column_drop_count + j;
3884
3885 /* No need to track the list */
3886 329 ctx->add_vcol[j].v_indexes = nullptr;
3887
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 innodb_base_col_setup(ctx->old_table, field, &ctx->add_vcol[j]);
3888 329 j++;
3889 }
3890
3891 316 return (false);
3892 }
3893
3894 /** Collect virtual column info for its addition
3895 @param[in] ha_alter_info Data used during in-place alter
3896 @param[in] table MySQL table as it is before the ALTER operation
3897 @retval true Failure
3898 @retval false Success */
3899 94 static bool prepare_inplace_drop_virtual(Alter_inplace_info *ha_alter_info,
3900 const TABLE *table) {
3901 ha_innobase_inplace_ctx *ctx;
3902 94 ulint j = 0;
3903
3904 94 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
3905
3906 188 ctx->drop_vcol = static_cast<dict_v_col_t *>(mem_heap_alloc(
3907 ctx->heap,
3908 94 ha_alter_info->virtual_column_drop_count * sizeof *ctx->drop_vcol));
3909 188 ctx->drop_vcol_name = static_cast<const char **>(mem_heap_alloc(
3910 ctx->heap,
3911 94 ha_alter_info->virtual_column_drop_count * sizeof *ctx->drop_vcol_name));
3912
3913
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 93 times.
264 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
3914 const Field *field;
3915 ulint old_i;
3916
3917
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 100 times.
171 if (drop->type != Alter_drop::COLUMN) continue;
3918
3919
1/2
✓ Branch 0 taken 483 times.
✗ Branch 1 not taken.
483 for (old_i = 0; table->field[old_i]; old_i++) {
3920 483 const Field *n_field = table->field[old_i];
3921
3/4
✓ Branch 0 taken 483 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 383 times.
483 if (!my_strcasecmp(system_charset_info, n_field->field_name,
3922 drop->name)) {
3923 100 break;
3924 }
3925 }
3926 /* SQL-layer already has checked that all columns to be dropped exist. */
3927
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 ut_ad(table->field[old_i]);
3928 100 field = table->field[old_i];
3929
3930 /*
3931 We don't support simultaneous removal of virtual and stored columns
3932 as in-place operation yet.
3933 */
3934
3/6
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 100 times.
100 ut_ad(field->gcol_info && !field->stored_in_db);
3935
3936 ulint col_len;
3937 ulint is_unsigned;
3938 ulint field_type;
3939 ulint charset_no;
3940
3941
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
3942
3943
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 bool is_multi_value = innobase_is_multi_value_fld(field);
3944
3945
2/4
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 100 times.
100 if (!field->gcol_info || field->stored_in_db) {
3946 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3947 1 return (true);
3948 }
3949
3950
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 61 times.
100 if (is_multi_value) {
3951
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 col_len = field->key_length();
3952 } else {
3953
1/2
✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
61 col_len = field->pack_length();
3954 }
3955
3956
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 field_type = (ulint)field->type();
3957
3958
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 98 times.
100 if (!field->is_nullable()) {
3959 2 field_type |= DATA_NOT_NULL;
3960 }
3961
3962
3/4
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 12 times.
100 if (field->binary()) {
3963 88 field_type |= DATA_BINARY_TYPE;
3964 }
3965
3966
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 96 times.
100 if (is_unsigned) {
3967 4 field_type |= DATA_UNSIGNED;
3968 }
3969
3970
3/4
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 64 times.
100 if (dtype_is_string_type(col_type)) {
3971
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 charset_no = (ulint)field->charset()->number;
3972
3973
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35 times.
36 DBUG_EXECUTE_IF("ib_alter_add_virtual_fail",
3974 charset_no += MAX_CHAR_COLL_NUM;);
3975
3976
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35 times.
36 if (charset_no > MAX_CHAR_COLL_NUM) {
3977
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3978 1 return (true);
3979 }
3980 } else {
3981 64 charset_no = 0;
3982 }
3983
3984
7/8
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 95 times.
99 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
3985
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 uint32_t length_bytes = field->get_length_bytes();
3986
3987 4 col_len -= length_bytes;
3988
3989
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (length_bytes == 2) {
3990 2 field_type |= DATA_LONG_TRUE_VARCHAR;
3991 }
3992 }
3993
3994
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 ctx->drop_vcol[j].m_col.prtype = dtype_form_prtype(field_type, charset_no);
3995
3996 99 ctx->drop_vcol[j].m_col.prtype |= DATA_VIRTUAL;
3997
3998 99 ctx->drop_vcol[j].m_col.mtype = col_type;
3999
4000 99 ctx->drop_vcol[j].m_col.len = col_len;
4001
4002 99 ctx->drop_vcol[j].m_col.ind = old_i;
4003
4004 99 ctx->drop_vcol_name[j] = field->field_name;
4005
4006
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 dict_v_col_t *v_col = dict_table_get_nth_v_col_mysql(ctx->old_table, old_i);
4007 99 ctx->drop_vcol[j].v_pos = v_col->v_pos;
4008 99 j++;
4009 }
4010
4011 93 return (false);
4012 }
4013
4014 /** Adjust the create index column number from "New table" to
4015 "old InnoDB table" while we are doing dropping virtual column. Since we do
4016 not create separate new table for the dropping/adding virtual columns.
4017 To correctly find the indexed column, we will need to find its col_no
4018 in the "Old Table", not the "New table".
4019 @param[in] ha_alter_info Data used during in-place alter
4020 @param[in] old_table MySQL table as it is before the ALTER operation
4021 @param[in] num_v_dropped number of virtual column dropped
4022 @param[in,out] index_def index definition */
4023 8 static void innodb_v_adjust_idx_col(const Alter_inplace_info *ha_alter_info,
4024 const TABLE *old_table, ulint num_v_dropped,
4025 ddl::Index_defn *index_def) {
4026 List_iterator_fast<Create_field> cf_it(
4027
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 ha_alter_info->alter_info->create_list);
4028
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8 times.
18 for (ulint i = 0; i < index_def->m_n_fields; i++) {
4029 #ifdef UNIV_DEBUG
4030 10 bool col_found = false;
4031 #endif /* UNIV_DEBUG */
4032 10 ulint num_v = 0;
4033
4034 10 auto index_field = &index_def->m_fields[i];
4035
4036 /* Only adjust virtual column col_no, since non-virtual
4037 column position (in non-vcol list) won't change unless
4038 table rebuild */
4039
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (!index_field->m_is_v_col) {
4040 1 continue;
4041 }
4042
4043 9 const Field *field = nullptr;
4044
4045 9 cf_it.rewind();
4046
4047 /* Found the field in the new table */
4048
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 while (const Create_field *new_field = cf_it++) {
4049
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 10 times.
27 if (!new_field->is_virtual_gcol()) {
4050 17 continue;
4051 }
4052
4053 10 field = new_field->field;
4054
4055
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 if (num_v == index_field->m_col_no) {
4056 9 break;
4057 }
4058 1 num_v++;
4059 18 }
4060
4061
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!field) {
4062 /* this means the field is a newly added field, this
4063 should have been blocked when we drop virtual column
4064 at the same time */
4065 ut_ad(num_v_dropped > 0);
4066 ut_error;
4067 }
4068
4069
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 ut_ad(field->is_virtual_gcol());
4070
4071 9 num_v = 0;
4072
4073 /* Look for its position in old table */
4074
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 for (uint old_i = 0; old_table->field[old_i]; old_i++) {
4075
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 27 times.
36 if (old_table->field[old_i] == field) {
4076 /* Found it, adjust its col_no to its position
4077 in old table */
4078 9 index_def->m_fields[i].m_col_no = num_v;
4079 9 ut_d(col_found = true);
4080 9 break;
4081 }
4082
4083
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 17 times.
27 if (old_table->field[old_i]->is_virtual_gcol()) {
4084 10 num_v++;
4085 }
4086 }
4087
4088
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 ut_ad(col_found);
4089 }
4090 8 }
4091
4092 /** Replace the table name in filename with the specified one
4093 @param[in] filename original file name
4094 @param[out] new_filename new file name
4095 @param[in] table_name to replace with this table name,
4096 in the format of db/name */
4097 17882 static void replace_table_name(const char *filename, char *new_filename,
4098 const char *table_name) {
4099 17882 const char *slash = strrchr(filename, OS_PATH_SEPARATOR);
4100 17882 size_t len = 0;
4101
4102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17882 times.
17882 if (slash == nullptr) {
4103 len = 0;
4104 } else {
4105 17882 len = slash - filename + 1;
4106 }
4107
4108 17882 memcpy(new_filename, filename, len);
4109
4110 17882 slash = strchr(table_name, '/');
4111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17882 times.
17882 ut_ad(slash != nullptr);
4112
4113 17882 strcpy(new_filename + len, slash + 1);
4114
4115 17882 len += strlen(slash + 1);
4116
4117 17882 strcpy(new_filename + len, dot_ext[IBD]);
4118 17882 }
4119
4120 /** Update the metadata in prepare phase. This only check if dd::Tablespace
4121 should be removed or(and) created, because to remove and store dd::Tablespace
4122 could fail, so it's better to do it earlier, to prevent a late rollback
4123 @param[in,out] thd MySQL connection
4124 @param[in] old_table Old InnoDB table object
4125 @param[in,out] new_table New InnoDB table object
4126 @param[in] old_dd_tab Old dd::Table or dd::Partition
4127 @return false On success
4128 @retval true On failure */
4129 template <typename Table>
4130 95032 [[nodiscard]] static bool dd_prepare_inplace_alter_table(
4131 THD *thd, const dict_table_t *old_table, dict_table_t *new_table,
4132 const Table *old_dd_tab) {
4133
7/8
✓ Branch 0 taken 47516 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47506 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 17146 times.
✓ Branch 5 taken 30360 times.
✓ Branch 6 taken 17156 times.
✓ Branch 7 taken 30360 times.
95032 if (new_table->is_temporary() || old_table == new_table) {
4134 /* No need to fill in metadata for temporary tables,
4135 which would not be stored in Global DD */
4136 34312 return false;
4137 }
4138
4139
1/2
✓ Branch 0 taken 30360 times.
✗ Branch 1 not taken.
60720 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
4140
1/2
✓ Branch 0 taken 30360 times.
✗ Branch 1 not taken.
60720 dd::cache::Dictionary_client::Auto_releaser releaser(client);
4141
4142 60720 uint64_t autoextend_size{};
4143
4144
3/4
✓ Branch 0 taken 30360 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19651 times.
✓ Branch 3 taken 10709 times.
60720 if (dict_table_is_file_per_table(old_table)) {
4145
2/4
✓ Branch 0 taken 19651 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19651 times.
✗ Branch 3 not taken.
39302 dd::Object_id old_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4146
4147 /* Copy the autoextend_size attribute value for the tablespace being
4148 dropped. This value will be copied to the new tablespace created later. */
4149
2/4
✓ Branch 0 taken 19651 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19651 times.
39302 if (dd_get_tablespace_size_option(client, old_space_id, &autoextend_size)) {
4150 return true;
4151 }
4152
4153
2/4
✓ Branch 0 taken 19651 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19651 times.
39302 if (dd_drop_tablespace(client, old_space_id)) {
4154 return true;
4155 }
4156 }
4157
4158
3/4
✓ Branch 0 taken 30360 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17882 times.
✓ Branch 3 taken 12478 times.
60720 if (dict_table_is_file_per_table(new_table)) {
4159 /* Replace the table name with the final correct one */
4160
1/2
✓ Branch 0 taken 17882 times.
✗ Branch 1 not taken.
35764 char *path = fil_space_get_first_path(new_table->space);
4161 char filename[FN_REFLEN + 1];
4162
1/2
✓ Branch 0 taken 17882 times.
✗ Branch 1 not taken.
35764 replace_table_name(path, filename, old_table->name.m_name);
4163 35764 ut::free(path);
4164
4165 35764 bool discarded = false;
4166
3/4
✓ Branch 0 taken 17882 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17765 times.
✓ Branch 3 taken 117 times.
35764 if (dict_table_is_file_per_table(old_table)) {
4167
1/2
✓ Branch 0 taken 17765 times.
✗ Branch 1 not taken.
35530 discarded = dd_is_discarded(*old_dd_tab);
4168 }
4169
4170 dd::Object_id dd_space_id;
4171
4172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17882 times.
35764 if (dd_create_implicit_tablespace(client, new_table->space,
4173
1/2
✓ Branch 0 taken 17882 times.
✗ Branch 1 not taken.
35764 old_table->name.m_name, filename,
4174 discarded, dd_space_id)) {
4175 my_error(ER_INTERNAL_ERROR, MYF(0),
4176 " InnoDB can't create tablespace object"
4177 " for ",
4178 new_table->name);
4179 return true;
4180 }
4181
4182 35764 new_table->dd_space_id = dd_space_id;
4183 }
4184
4185 60720 return false;
4186 60720 }
4187
4188 /** Update table level instant metadata in commit phase of INPLACE ALTER
4189 @param[in] table InnoDB table object
4190 @param[in] old_dd_tab old dd::Table
4191 @param[in] new_dd_tab new dd::Table */
4192 16562 static void dd_commit_inplace_update_instant_meta(const dict_table_t *table,
4193 const dd::Table *old_dd_tab,
4194 dd::Table *new_dd_tab) {
4195 /** If table->skip_alter_undo is true during inplace, it is expanded fast
4196 index creation. The inplace ALTERs for that can only be about DROP INDEX
4197 and ADD INDEX and can never be instant operations */
4198
6/6
✓ Branch 0 taken 16553 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 16547 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 16556 times.
✓ Branch 5 taken 6 times.
16562 if (table->skip_alter_undo || !dd_table_has_instant_cols(*old_dd_tab)) {
4199 16556 return;
4200 }
4201
4202
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 ut_ad(table->has_instant_cols() || table->has_row_versions());
4203
4204 6 const char *s = dd_table_key_strings[DD_TABLE_INSTANT_COLS];
4205
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (old_dd_tab->se_private_data().exists(s)) {
4206 ut_ad(table->is_upgraded_instant());
4207 uint32_t value = 0;
4208 old_dd_tab->se_private_data().get(s, &value);
4209 new_dd_tab->se_private_data().set(s, value);
4210 }
4211
4212 /* Copy instant default values of columns if exists */
4213
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 6 times.
66 for (uint16_t i = 0; i < table->get_n_user_cols(); ++i) {
4214 60 const dict_col_t *col = table->get_col(i);
4215
4216
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 6 times.
60 if (col->instant_default == nullptr) {
4217 54 continue;
4218 }
4219
4220 dd::Column *dd_col = const_cast<dd::Column *>(
4221 6 dd_find_column(new_dd_tab, table->get_col_name(i)));
4222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 ut_ad(dd_col != nullptr);
4223
4224 6 dd_write_default_value(col, dd_col);
4225 }
4226 }
4227
4228 /** Update metadata in commit phase. Note this function should only update
4229 the metadata which would not result in failure
4230 @param[in] old_info Some table information for the old table
4231 @param[in,out] new_table New InnoDB table object
4232 @param[in] old_dd_tab Old dd::Table or dd::Partition
4233 @param[in,out] new_dd_tab New dd::Table or dd::Partition */
4234 template <typename Table>
4235 94142 static void dd_commit_inplace_alter_table(
4236 const alter_table_old_info_t &old_info, dict_table_t *new_table,
4237 const Table *old_dd_tab, Table *new_dd_tab) {
4238
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 47062 times.
94142 if (new_table->is_temporary()) {
4239 /* No need to fill in metadata for temporary tables,
4240 which would not be stored in Global DD */
4241 18 return;
4242 }
4243
4244 dd::Object_id dd_space_id;
4245
4246
2/2
✓ Branch 0 taken 30071 times.
✓ Branch 1 taken 16991 times.
94124 if (old_info.m_rebuild) {
4247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30071 times.
60142 ut_ad(!new_table->has_instant_cols());
4248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30071 times.
60142 ut_ad(!new_table->has_row_versions());
4249
4250
2/2
✓ Branch 0 taken 17611 times.
✓ Branch 1 taken 12460 times.
60142 if (dict_table_is_file_per_table(new_table)) {
4251 /* Get the one created in prepare phase */
4252 35222 dd_space_id = new_table->dd_space_id;
4253
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 12368 times.
24920 } else if (new_table->space == TRX_SYS_SPACE) {
4254 184 dd_space_id = dict_sys_t::s_dd_sys_space_id;
4255 } else {
4256 /* Currently, even if specifying a new TABLESPACE
4257 for partitioned table, existing partitions would not
4258 be moved to new tablespaces. Thus, the old
4259 tablespace id should still be used for new partition */
4260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12368 times.
24736 if (dd_table_is_partitioned(new_dd_tab->table())) {
4261 dd_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4262 } else {
4263 24736 dd_space_id = dd_get_space_id(new_dd_tab->table());
4264 }
4265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12368 times.
24736 ut_ad(dd_space_id != dd::INVALID_OBJECT_ID);
4266 }
4267 } else {
4268
4/4
✓ Branch 0 taken 433 times.
✓ Branch 1 taken 16558 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 16905 times.
34848 if (old_info.m_fts_doc_id &&
4269
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 347 times.
866 !dd_find_column(&new_dd_tab->table(), FTS_DOC_ID_COL_NAME)) {
4270 dd::Column *col =
4271 172 dd_add_hidden_column(&new_dd_tab->table(), FTS_DOC_ID_COL_NAME,
4272 FTS_DOC_ID_LEN, dd::enum_column_types::LONGLONG);
4273
4274 172 dd_set_hidden_unique_index(new_dd_tab->table().add_index(),
4275 FTS_DOC_ID_INDEX_NAME, col);
4276 }
4277
4278 /* This can happen only with expanded fast index creation. On the
4279 intermediate table during ALTER COPY, we drop secondary indexes using
4280 inplace alter APIs. The old definition here is old copy of table. Hence we
4281 should use new_dd_tab here for updating the dd::Indexes */
4282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16991 times.
33982 if (new_table->skip_alter_undo) {
4283 dd_space_id = dd_first_index(new_dd_tab)->tablespace_id();
4284 } else {
4285 33982 dd_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4286 }
4287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16991 times.
33982 ut_ad(dd_space_id != dd::INVALID_OBJECT_ID);
4288 }
4289
4290 94124 dd_set_table_options(new_dd_tab, new_table);
4291
4292 94124 new_table->dd_space_id = dd_space_id;
4293
4294 94124 dd_write_table(dd_space_id, new_dd_tab, new_table);
4295
4296 /* If this table is discarded, we need to set this to both dd::Table
4297 and dd::Tablespace. */
4298
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 47051 times.
94124 if (old_info.m_discarded) {
4299
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 dd_set_discarded(*new_dd_tab, true);
4300
4301
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 THD *thd = current_thd;
4302 dd::Object_id dd_space_id =
4303
4/8
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
22 (*new_dd_tab->indexes()->begin())->tablespace_id();
4304
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 std::string space_name(new_table->name.m_name);
4305
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 dict_name::convert_to_space(space_name);
4306
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
22 dd_tablespace_set_state(thd, dd_space_id, space_name,
4307 DD_SPACE_STATE_DISCARDED);
4308 }
4309 }
4310
4311 template <typename Table>
4312 static void dd_commit_inplace_no_change(const Alter_inplace_info *ha_alter_info,
4313 const Table *old_dd_tab,
4314 Table *new_dd_tab, bool ignore_fts) {
4315 if (!ignore_fts) {
4316 dd_add_fts_doc_id_index(new_dd_tab->table(), old_dd_tab->table());
4317 }
4318
4319 dd_copy_private(*new_dd_tab, *old_dd_tab);
4320
4321 if (!dd_table_is_partitioned(new_dd_tab->table()) ||
4322 dd_part_is_first(reinterpret_cast<dd::Partition *>(new_dd_tab))) {
4323 dd_copy_table(ha_alter_info, new_dd_tab->table(), old_dd_tab->table());
4324 }
4325 }
4326
4327 /** Check if a new table's index will exceed the index limit for the table
4328 row format
4329 @param[in] form MySQL table that is being altered
4330 @param[in] max_len max index length allowed
4331 @return true if within limits false otherwise */
4332 37 static bool innobase_check_index_len(const TABLE *form, ulint max_len) {
4333
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 37 times.
59 for (uint key_num = 0; key_num < form->s->keys; key_num++) {
4334 22 const KEY &key = form->key_info[key_num];
4335
4336
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 22 times.
47 for (unsigned i = 0; i < key.user_defined_key_parts; i++) {
4337 25 const KEY_PART_INFO *key_part = &key.key_part[i];
4338 25 unsigned prefix_len = 0;
4339
4340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (key.flags & HA_SPATIAL) {
4341 prefix_len = 0;
4342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 } else if (key.flags & HA_FULLTEXT) {
4343 prefix_len = 0;
4344
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25 } else if (key_part->key_part_flag & HA_PART_KEY_SEG) {
4345 /* SPATIAL and FULLTEXT index always are on
4346 full columns. */
4347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(!(key.flags & (HA_SPATIAL | HA_FULLTEXT)));
4348 3 prefix_len = key_part->length;
4349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(prefix_len > 0);
4350 } else {
4351 22 prefix_len = 0;
4352 }
4353
4354
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if (key_part->length > max_len || prefix_len > max_len) {
4355 return (false);
4356 }
4357 }
4358 }
4359 37 return (true);
4360 }
4361
4362 /** Update internal structures with concurrent writes blocked,
4363 while preparing ALTER TABLE.
4364
4365 @param ha_alter_info Data used during in-place alter
4366 @param altered_table MySQL table that is being altered
4367 @param old_table MySQL table as it is before the ALTER operation
4368 @param old_dd_tab old dd table
4369 @param new_dd_tab new dd table
4370 @param table_name Table name in MySQL
4371 @param flags Table and tablespace flags
4372 @param flags2 Additional table flags
4373 @param fts_doc_id_col The column number of FTS_DOC_ID
4374 @param add_fts_doc_id Flag: add column FTS_DOC_ID?
4375 @param add_fts_doc_id_idx Flag: add index FTS_DOC_ID_INDEX (FTS_DOC_ID)?
4376
4377 @retval true Failure
4378 @retval false Success */
4379 template <typename Table>
4380 71524 [[nodiscard]] static bool prepare_inplace_alter_table_dict(
4381 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
4382 const TABLE *old_table, const Table *old_dd_tab, Table *new_dd_tab,
4383 const char *table_name, uint32_t flags, uint32_t flags2,
4384 ulint fts_doc_id_col, bool add_fts_doc_id, bool add_fts_doc_id_idx,
4385 row_prebuilt_t *prebuilt) {
4386 71524 bool dict_locked = false;
4387 ulint *add_key_nums; /* MySQL key numbers */
4388 ddl::Index_defn *index_defs; /* index definitions */
4389 dict_table_t *user_table;
4390 71524 dict_index_t *fts_index = nullptr;
4391 dberr_t error;
4392 ulint num_fts_index;
4393 71524 dict_add_v_col_t *add_v = nullptr;
4394 dict_table_t *table;
4395 71524 MDL_ticket *mdl = nullptr;
4396
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 THD *thd = current_thd;
4397 71524 bool build_fts_common = false;
4398
4399 ha_innobase_inplace_ctx *ctx;
4400 71524 KeyringEncryptionKeyIdInfo keyring_encryption_key_id;
4401 71524 bool none_explicitly_specified = Encryption::none_explicitly_specified(
4402 71524 ha_alter_info->create_info->explicit_encryption,
4403 71524 ha_alter_info->create_info->encrypt_type.str);
4404
4405
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 DBUG_TRACE;
4406
4407 71524 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
4408
4409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert((ctx->add_autoinc != ULINT_UNDEFINED) ==
4410 (ctx->sequence.m_max_value > 0));
4411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->num_to_drop_index == !ctx->drop_index);
4412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->num_to_drop_fk == !ctx->drop_fk);
4413
3/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 35640 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
71524 assert(!add_fts_doc_id || add_fts_doc_id_idx);
4414
3/4
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 35625 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
71524 assert(!add_fts_doc_id_idx || innobase_fulltext_exist(altered_table));
4415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->add_cols);
4416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->add_index);
4417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->add_key_numbers);
4418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ctx->num_to_add_index);
4419
4420 71524 user_table = ctx->new_table;
4421
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 bool is_file_per_table = dict_table_is_file_per_table(user_table);
4422
4423
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 trx_start_if_not_started_xa(ctx->prebuilt->trx, true, UT_LOCATION_HERE);
4424
4425
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 35755 times.
71524 if (ha_alter_info->handler_flags & Alter_inplace_info::DROP_VIRTUAL_COLUMN) {
4426
3/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6 times.
14 if (prepare_inplace_drop_virtual(ha_alter_info, old_table)) {
4427 2 return true;
4428 }
4429 }
4430
4431
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 35591 times.
71522 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_VIRTUAL_COLUMN) {
4432
3/4
✓ Branch 0 taken 170 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 169 times.
340 if (prepare_inplace_add_virtual(ha_alter_info, altered_table, old_table)) {
4433 2 return true;
4434 }
4435
4436 /* Need information for newly added virtual columns
4437 for create index */
4438
1/2
✓ Branch 0 taken 169 times.
✗ Branch 1 not taken.
338 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX) {
4439
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 169 times.
702 for (ulint i = 0; i < ha_alter_info->virtual_column_add_count; i++) {
4440 /* Set mbminmax for newly added column */
4441 ulint i_mbminlen, i_mbmaxlen;
4442 364 dtype_get_mblen(ctx->add_vcol[i].m_col.mtype,
4443
1/2
✓ Branch 0 taken 182 times.
✗ Branch 1 not taken.
364 ctx->add_vcol[i].m_col.prtype, &i_mbminlen,
4444 &i_mbmaxlen);
4445
4446
1/2
✓ Branch 0 taken 182 times.
✗ Branch 1 not taken.
364 ctx->add_vcol[i].m_col.set_mbminmaxlen(i_mbminlen, i_mbmaxlen);
4447 }
4448 add_v = static_cast<dict_add_v_col_t *>(
4449
1/2
✓ Branch 0 taken 169 times.
✗ Branch 1 not taken.
338 mem_heap_alloc(ctx->heap, sizeof *add_v));
4450 338 add_v->n_v_col = ha_alter_info->virtual_column_add_count;
4451 338 add_v->v_col = ctx->add_vcol;
4452 338 add_v->v_col_name = ctx->add_vcol_name;
4453 }
4454 }
4455
4456
2/2
✓ Branch 0 taken 15049 times.
✓ Branch 1 taken 20711 times.
71520 if ((ha_alter_info->handler_flags &
4457 30098 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
4458
2/2
✓ Branch 0 taken 10320 times.
✓ Branch 1 taken 4729 times.
30098 !(ha_alter_info->create_info->used_fields & HA_CREATE_USED_TABLESPACE) &&
4459 20640 ha_alter_info->create_info
4460
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10318 times.
20640 ->m_implicit_tablespace_autoextend_size_change) {
4461 /* Update the autoextend_size value in the data dictionary. Do not update
4462 if the table is being moved to a new tablespace. The autoextend_size value
4463 for the new tablespace will be updated later. */
4464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (prepare_inplace_change_implicit_tablespace_option(
4465
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 ctx->prebuilt->trx->mysql_thd, ha_alter_info, ctx->old_table)) {
4466 return true;
4467 }
4468 }
4469
4470 /* There should be no order change for virtual columns coming in
4471 here */
4472
2/4
✓ Branch 0 taken 35760 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35760 times.
71520 ut_ad(check_v_col_in_order(old_table, altered_table, ha_alter_info));
4473
4474 71520 ctx->trx = ctx->prebuilt->trx;
4475
4476 /* Create table containing all indexes to be built in this
4477 ALTER TABLE ADD INDEX so that they are in the correct order
4478 in the table. */
4479
4480 71520 ctx->num_to_add_index = ha_alter_info->index_add_count;
4481
4482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35760 times.
71520 ut_ad(ctx->prebuilt->trx->mysql_thd != nullptr);
4483
1/2
✓ Branch 0 taken 35760 times.
✗ Branch 1 not taken.
71520 const char *path = thd_innodb_tmpdir(ctx->prebuilt->trx->mysql_thd);
4484
4485 214560 index_defs = innobase_create_key_defs(
4486 ctx->heap, ha_alter_info, altered_table, new_dd_tab,
4487 71520 ctx->num_to_add_index, num_fts_index,
4488
2/4
✓ Branch 0 taken 35760 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35760 times.
✗ Branch 3 not taken.
71520 row_table_got_default_clust_index(ctx->new_table), fts_doc_id_col,
4489 add_fts_doc_id, add_fts_doc_id_idx, old_table, is_file_per_table);
4490
4491 71520 bool new_clustered = DICT_CLUSTERED & index_defs[0].m_ind_type;
4492
4493
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35758 times.
71520 if (num_fts_index > 1) {
4494
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 my_error(ER_INNODB_FT_LIMIT, MYF(0));
4495 4 goto error_handled;
4496 }
4497
4498
2/2
✓ Branch 0 taken 30467 times.
✓ Branch 1 taken 5291 times.
71516 if (new_clustered) {
4499 /* If max index length is reduced due to row format change
4500 make sure the index can all be accomodated in new row format */
4501 60934 ulint max_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags);
4502
4503
4/4
✓ Branch 0 taken 30292 times.
✓ Branch 1 taken 175 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 30430 times.
60934 if (max_len < DICT_MAX_FIELD_LEN_BY_FORMAT(ctx->old_table)) {
4504
2/4
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
74 if (!innobase_check_index_len(altered_table, max_len)) {
4505 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_len);
4506 goto error_handled;
4507 }
4508 }
4509 }
4510
4511
2/2
✓ Branch 0 taken 34869 times.
✓ Branch 1 taken 889 times.
71516 if (!ctx->online) {
4512 /* This is not an online operation (LOCK=NONE). */
4513
3/6
✓ Branch 0 taken 34869 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34869 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 34869 times.
139476 } else if (ctx->add_autoinc == ULINT_UNDEFINED && num_fts_index == 0 &&
4514
2/2
✓ Branch 0 taken 30175 times.
✓ Branch 1 taken 4694 times.
69738 (!innobase_need_rebuild(ha_alter_info, old_table,
4515 60350 is_file_per_table) ||
4516
1/2
✓ Branch 0 taken 30175 times.
✗ Branch 1 not taken.
60350 !innobase_fulltext_exist(altered_table))) {
4517 /* InnoDB can perform an online operation (LOCK=NONE). */
4518 } else {
4519 /* This should have been blocked in
4520 check_if_supported_inplace_alter(). */
4521 my_error(ER_NOT_SUPPORTED_YET, MYF(0),
4522 thd_query_unsafe(ctx->prebuilt->trx->mysql_thd).str);
4523 ut_d(ut_error);
4524 ut_o(goto error_handled);
4525 }
4526
4527 /* The primary index would be rebuilt if a FTS Doc ID
4528 column is to be added, and the primary index definition
4529 is just copied from old table and stored in indexdefs[0] */
4530
3/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 35636 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
71516 assert(!add_fts_doc_id || new_clustered);
4531
5/6
✓ Branch 0 taken 5410 times.
✓ Branch 1 taken 30348 times.
✓ Branch 2 taken 119 times.
✓ Branch 3 taken 5291 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 35758 times.
71516 assert(
4532 new_clustered ==
4533 (innobase_need_rebuild(ha_alter_info, old_table, is_file_per_table) ||
4534 add_fts_doc_id));
4535
4536 /* Allocate memory for dictionary index definitions */
4537
4538 143032 ctx->add_index = static_cast<dict_index_t **>(mem_heap_alloc(
4539
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 ctx->heap, ctx->num_to_add_index * sizeof *ctx->add_index));
4540 143032 ctx->add_key_numbers = add_key_nums = static_cast<ulint *>(mem_heap_alloc(
4541
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 ctx->heap, ctx->num_to_add_index * sizeof *ctx->add_key_numbers));
4542
4543 /* Acquire a lock on the table before creating any indexes. */
4544
2/2
✓ Branch 0 taken 34869 times.
✓ Branch 1 taken 889 times.
71516 if (ctx->online) {
4545 69738 error = DB_SUCCESS;
4546 } else {
4547 1778 error = ddl::lock_table(ctx->prebuilt->trx, ctx->new_table, LOCK_S);
4548
4549
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 889 times.
1778 if (error != DB_SUCCESS) {
4550 goto error_handling;
4551 }
4552 }
4553
4554 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
4555 or lock waits can happen in it during an index create operation. */
4556
4557
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 row_mysql_lock_data_dictionary(ctx->prebuilt->trx, UT_LOCATION_HERE);
4558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35758 times.
71516 ut_ad(ctx->trx == ctx->prebuilt->trx);
4559 71516 dict_locked = true;
4560
4561 /* Wait for background stats processing to stop using the table that
4562 we are going to alter. We know bg stats will not start using it again
4563 until we are holding the data dict locked and we are holding it here
4564 at least until checking ut_ad(user_table->n_ref_count == 1) below.
4565 XXX what may happen if bg stats opens the table after we
4566 have unlocked data dictionary below? */
4567
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 dict_stats_wait_bg_to_stop_using_table(user_table, ctx->trx);
4568
4569
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 online_retry_drop_dict_indexes(ctx->new_table, true);
4570
4571
1/2
✓ Branch 0 taken 35758 times.
✗ Branch 1 not taken.
71516 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ABORTED_OK));
4572
4573 /* If a new clustered index is defined for the table we need
4574 to rebuild the table with a temporary name. */
4575
4576
2/2
✓ Branch 0 taken 30467 times.
✓ Branch 1 taken 5291 times.
71516 if (new_clustered) {
4577 121868 const char *new_table_name = dict_mem_create_temporary_tablename(
4578
1/2
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
60934 ctx->heap, ctx->new_table->name.m_name, ctx->new_table->id);
4579 60934 ulint n_cols = 0;
4580 60934 ulint n_v_cols = 0;
4581 60934 ulint n_m_v_cols = 0;
4582 dtuple_t *add_cols;
4583 60934 space_id_t space_id = 0;
4584 60934 ulint key_id = FIL_DEFAULT_ENCRYPTION_KEY;
4585 60934 fil_encryption_t mode = FIL_ENCRYPTION_DEFAULT;
4586
4587 /* SQL-layer already has checked that we are not dropping any
4588 columns in foreign keys to be kept or making referencing column
4589 in a foreign key with SET NULL action non-nullable. So no need to
4590 check this here. */
4591
4592
2/2
✓ Branch 0 taken 226070 times.
✓ Branch 1 taken 30467 times.
513074 for (uint i = 0; i < altered_table->s->fields; i++) {
4593 452140 const Field *field = altered_table->field[i];
4594
4595
4/4
✓ Branch 0 taken 859 times.
✓ Branch 1 taken 225211 times.
✓ Branch 2 taken 837 times.
✓ Branch 3 taken 22 times.
452140 if (innobase_is_v_fld(field)) {
4596 1674 n_v_cols++;
4597
3/4
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 835 times.
1674 if (innobase_is_multi_value_fld(field)) {
4598 4 n_m_v_cols++;
4599 }
4600 } else {
4601 450466 n_cols++;
4602 }
4603 }
4604
4605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30467 times.
60934 ut_ad(n_cols + n_v_cols == altered_table->s->fields);
4606
4607
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 30345 times.
60934 if (add_fts_doc_id) {
4608 244 n_cols++;
4609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
244 assert(flags2 & DICT_TF2_FTS);
4610
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
244 assert(add_fts_doc_id_idx);
4611 244 flags2 |=
4612 DICT_TF2_FTS_ADD_DOC_ID | DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS;
4613 }
4614
4615
3/4
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 30344 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
60934 assert(!add_fts_doc_id_idx || (flags2 & DICT_TF2_FTS));
4616
4617 /* Create the table. */
4618
1/2
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
60934 table = dd_table_open_on_name(thd, &mdl, new_table_name, true,
4619 DICT_ERR_IGNORE_NONE);
4620
4621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30467 times.
60934 if (table) {
4622 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_table_name);
4623 dd_table_close(table, thd, &mdl, true);
4624 goto new_clustered_failed;
4625 }
4626
4627 /* Use the old tablespace unless the tablespace
4628 is changing. */
4629
4/4
✓ Branch 0 taken 10695 times.
✓ Branch 1 taken 19772 times.
✓ Branch 2 taken 10478 times.
✓ Branch 3 taken 19989 times.
82324 if (DICT_TF_HAS_SHARED_SPACE(user_table->flags) &&
4630
1/2
✓ Branch 0 taken 10695 times.
✗ Branch 1 not taken.
21390 (ha_alter_info->create_info->tablespace == nullptr ||
4631
2/2
✓ Branch 0 taken 10478 times.
✓ Branch 1 taken 217 times.
21390 (0 == strcmp(ha_alter_info->create_info->tablespace,
4632 user_table->tablespace)))) {
4633 20956 space_id = user_table->space;
4634
2/2
✓ Branch 0 taken 2012 times.
✓ Branch 1 taken 17977 times.
39978 } else if (tablespace_is_shared_space(ha_alter_info->create_info)) {
4635 space_id =
4636
1/2
✓ Branch 0 taken 2012 times.
✗ Branch 1 not taken.
4024 fil_space_get_id_by_name(ha_alter_info->create_info->tablespace);
4637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2012 times.
4024 ut_a(space_id != SPACE_UNKNOWN);
4638 }
4639
4640 /* The initial space id 0 may be overridden later if this
4641 table is going to be a file_per_table tablespace. */
4642 60934 ctx->new_table =
4643
1/2
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
60934 dict_mem_table_create(new_table_name, space_id, n_cols + n_v_cols,
4644 n_v_cols, n_m_v_cols, flags, flags2);
4645
4646 /* TODO: Fix this problematic assignment */
4647
1/2
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
60934 ctx->new_table->dd_space_id = new_dd_tab->tablespace_id();
4648
4649 /* The rebuilt indexed_table will use the renamed
4650 column names. */
4651 60934 ctx->col_names = nullptr;
4652
4653
2/2
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 30319 times.
60934 if (DICT_TF_HAS_DATA_DIR(flags)) {
4654 592 ctx->new_table->data_dir_path =
4655
1/2
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
296 mem_heap_strdup(ctx->new_table->heap, user_table->data_dir_path);
4656 }
4657
4658
2/2
✓ Branch 0 taken 226070 times.
✓ Branch 1 taken 30467 times.
513074 for (uint i = 0; i < altered_table->s->fields; i++) {
4659 452140 const Field *field = altered_table->field[i];
4660 ulint is_unsigned;
4661
1/2
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
452140 ulint field_type = (ulint)field->type();
4662
1/2
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
452140 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
4663 ulint charset_no;
4664 ulint col_len;
4665
4/4
✓ Branch 0 taken 859 times.
✓ Branch 1 taken 225211 times.
✓ Branch 2 taken 837 times.
✓ Branch 3 taken 22 times.
452140 bool is_virtual = innobase_is_v_fld(field);
4666
1/2
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
452140 bool is_multi_value = innobase_is_multi_value_fld(field);
4667
4668 /* we assume in dtype_form_prtype() that this
4669 fits in two bytes */
4670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 226070 times.
452140 ut_a(field_type <= MAX_CHAR_COLL_NUM);
4671
4672
2/2
✓ Branch 0 taken 156748 times.
✓ Branch 1 taken 69322 times.
452140 if (!field->is_nullable()) {
4673 313496 field_type |= DATA_NOT_NULL;
4674 }
4675
4676
3/4
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 97192 times.
✓ Branch 3 taken 128878 times.
452140 if (field->binary()) {
4677 194384 field_type |= DATA_BINARY_TYPE;
4678 }
4679
4680
2/2
✓ Branch 0 taken 85878 times.
✓ Branch 1 taken 140192 times.
452140 if (is_unsigned) {
4681 171756 field_type |= DATA_UNSIGNED;
4682 }
4683
4684
3/4
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 91200 times.
✓ Branch 3 taken 134870 times.
452140 if (dtype_is_string_type(col_type)) {
4685
1/2
✓ Branch 0 taken 91200 times.
✗ Branch 1 not taken.
182400 charset_no = (ulint)field->charset()->number;
4686
4687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91200 times.
182400 if (charset_no > MAX_CHAR_COLL_NUM) {
4688 dict_mem_table_free(ctx->new_table);
4689 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
4690 goto new_clustered_failed;
4691 }
4692 } else {
4693 269740 charset_no = 0;
4694 }
4695
4696
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 226068 times.
452140 if (is_multi_value) {
4697
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 col_len = field->key_length();
4698 } else {
4699
1/2
✓ Branch 0 taken 226068 times.
✗ Branch 1 not taken.
452136 col_len = field->pack_length();
4700 }
4701
4702 /* The MySQL pack length contains 1 or 2 bytes
4703 length field for a true VARCHAR. Let us
4704 subtract that, so that the InnoDB column
4705 length in the InnoDB data dictionary is the
4706 real maximum byte length of the actual data. */
4707
4708
7/8
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9929 times.
✓ Branch 3 taken 216141 times.
✓ Branch 4 taken 9927 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 9927 times.
✓ Branch 7 taken 216143 times.
452140 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
4709
1/2
✓ Branch 0 taken 9927 times.
✗ Branch 1 not taken.
19854 uint32_t length_bytes = field->get_length_bytes();
4710
4711 19854 col_len -= length_bytes;
4712
4713
2/2
✓ Branch 0 taken 4517 times.
✓ Branch 1 taken 5410 times.
19854 if (length_bytes == 2) {
4714 9034 field_type |= DATA_LONG_TRUE_VARCHAR;
4715 }
4716 }
4717
4718
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 226065 times.
452140 if (field->column_format() == COLUMN_FORMAT_TYPE_COMPRESSED)
4719 10 field_type |= DATA_COMPRESSED;
4720
4721
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 226070 times.
452140 if (col_type == DATA_POINT) {
4722 /* DATA_POINT should be of fixed length,
4723 instead of the pack_length(blob length). */
4724 col_len = DATA_POINT_LEN;
4725 }
4726
4727
2/4
✓ Branch 0 taken 226070 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 226070 times.
452140 if (dict_col_name_is_reserved(field->field_name)) {
4728 dict_mem_table_free(ctx->new_table);
4729 my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name);
4730 goto new_clustered_failed;
4731 }
4732
4733
2/2
✓ Branch 0 taken 837 times.
✓ Branch 1 taken 225233 times.
452140 if (is_virtual) {
4734 1674 field_type |= DATA_VIRTUAL;
4735
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 835 times.
1674 if (is_multi_value) {
4736 4 field_type |= DATA_MULTI_VALUE;
4737 }
4738
1/2
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
1674 dict_mem_table_add_v_col(
4739
1/2
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
1674 ctx->new_table, ctx->heap, field->field_name, col_type,
4740 dtype_form_prtype(field_type, charset_no), col_len, i,
4741 1674 field->gcol_info->non_virtual_base_columns(),
4742
1/2
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
1674 !field->is_hidden_by_system());
4743 } else {
4744 900932 dict_mem_table_add_col(
4745
1/2
✓ Branch 0 taken 225233 times.
✗ Branch 1 not taken.
450466 ctx->new_table, ctx->heap, field->field_name, col_type,
4746 dtype_form_prtype(field_type, charset_no), col_len,
4747
2/4
✓ Branch 0 taken 225233 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 225233 times.
✗ Branch 3 not taken.
450466 !field->is_hidden_by_system(), UINT32_UNDEFINED, UINT8_UNDEFINED,
4748 UINT8_UNDEFINED);
4749 }
4750 }
4751
4752
2/2
✓ Branch 0 taken 823 times.
✓ Branch 1 taken 29644 times.
60934 if (n_v_cols) {
4753 1646 ulint z = 0;
4754
2/2
✓ Branch 0 taken 4879 times.
✓ Branch 1 taken 823 times.
11404 for (uint i = 0; i < altered_table->s->fields; i++) {
4755 dict_v_col_t *v_col;
4756 9758 const Field *field = altered_table->field[i];
4757
4758
4/4
✓ Branch 0 taken 859 times.
✓ Branch 1 taken 4020 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 837 times.
9758 if (!innobase_is_v_fld(field)) {
4759 8084 continue;
4760 }
4761
1/2
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
1674 v_col = dict_table_get_nth_v_col(ctx->new_table, z);
4762 1674 z++;
4763
1/2
✓ Branch 0 taken 837 times.
✗ Branch 1 not taken.
1674 innodb_base_col_setup(ctx->new_table, field, v_col);
4764 }
4765 }
4766
4767 /* Populate row version and column counts for new table */
4768 60934 ctx->new_table->current_row_version = 0;
4769 60934 ctx->new_table->initial_col_count = altered_table->s->fields - n_v_cols;
4770 60934 ctx->new_table->current_col_count = ctx->new_table->initial_col_count;
4771 60934 ctx->new_table->total_col_count = ctx->new_table->initial_col_count;
4772
4773
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 30345 times.
60934 if (add_fts_doc_id) {
4774
1/2
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
244 fts_add_doc_id_column(ctx->new_table, ctx->heap);
4775 244 ctx->new_table->fts->doc_col = fts_doc_id_col;
4776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
244 ut_ad(fts_doc_id_col == altered_table->s->fields - n_v_cols);
4777
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30344 times.
60690 } else if (ctx->new_table->fts) {
4778 2 ctx->new_table->fts->doc_col = fts_doc_id_col;
4779 }
4780
4781 const char *compression;
4782
4783 60934 compression = ha_alter_info->create_info->compress.str;
4784
4785
2/4
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30467 times.
60934 if (Compression::validate(compression) != DB_SUCCESS) {
4786 compression = nullptr;
4787 }
4788
4789 const char *encrypt;
4790 60934 encrypt = ha_alter_info->create_info->encrypt_type.str;
4791 60934 key_id = ha_alter_info->create_info->encryption_key_id;
4792
4793 // re-encrypting, check that key used to encrypt table is present
4794
2/2
✓ Branch 0 taken 626 times.
✓ Branch 1 taken 29841 times.
60934 if (DICT_TF2_FLAG_IS_SET(ctx->old_table,
4795 DICT_TF2_ENCRYPTION_FILE_PER_TABLE)) {
4796
2/2
✓ Branch 0 taken 624 times.
✓ Branch 1 taken 2 times.
1252 if (Encryption::is_master_key_encryption(
4797 1252 old_table->s->encrypt_type.str)) {
4798 // re-encrypting from master key encryption
4799 /* Check if keyring is ready. */
4800 1248 byte *master_key = NULL;
4801 uint32_t master_key_id;
4802
4803 1248 Encryption::get_master_key(&master_key_id, &master_key);
4804
4805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 624 times.
1248 if (master_key == NULL) {
4806 dict_mem_table_free(ctx->new_table);
4807 my_error(ER_CANNOT_FIND_KEY_IN_KEYRING, MYF(0));
4808 goto new_clustered_failed;
4809 } else {
4810
1/2
✓ Branch 0 taken 624 times.
✗ Branch 1 not taken.
1248 my_free(master_key);
4811 }
4812 }
4813 }
4814
4815
2/2
✓ Branch 0 taken 12070 times.
✓ Branch 1 taken 18397 times.
60934 if (none_explicitly_specified)
4816 24140 mode = FIL_ENCRYPTION_OFF;
4817 36794 else if (Encryption::is_keyring(encrypt) ||
4818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18397 times.
36794 (srv_default_table_encryption ==
4819 DEFAULT_TABLE_ENC_ONLINE_TO_KEYRING &&
4820 !none_explicitly_specified &&
4821
2/6
✓ Branch 0 taken 18397 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18397 times.
73588 !Encryption::is_master_key_encryption(encrypt)) ||
4822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18397 times.
36794 ha_alter_info->create_info->was_encryption_key_id_set) {
4823 mode = Encryption::is_keyring(encrypt) ? FIL_ENCRYPTION_ON
4824 : FIL_ENCRYPTION_DEFAULT;
4825 uint tablespace_key_version;
4826 byte *tablespace_key;
4827
4828 // TODO: Add checking for error returned from keyring function, not only
4829 // checking if tablespace is null
4830 Encryption::get_latest_key_or_create(
4831 key_id, server_uuid, &tablespace_key_version, &tablespace_key);
4832 if (tablespace_key == NULL) {
4833 dict_mem_table_free(ctx->new_table);
4834 my_printf_error(
4835 ER_ILLEGAL_HA_CREATE_OPTION,
4836 "Seems that keyring is down. It is not possible to encrypt table"
4837 " without keyring. Please install a keyring and try again.",
4838 MYF(0));
4839 goto new_clustered_failed;
4840 } else {
4841 my_free(tablespace_key);
4842 }
4843
4844 if (mode == FIL_ENCRYPTION_ON ||
4845 (mode == FIL_ENCRYPTION_DEFAULT &&
4846 srv_default_table_encryption ==
4847 DEFAULT_TABLE_ENC_ONLINE_TO_KEYRING)) {
4848 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
4849 }
4850 78124 } else if (!(ctx->new_table->flags2 & DICT_TF2_USE_FILE_PER_TABLE) &&
4851
3/4
✓ Branch 0 taken 2268 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 388 times.
✓ Branch 3 taken 1880 times.
9072 ha_alter_info->create_info->encrypt_type.length > 0 &&
4852
3/4
✓ Branch 0 taken 2268 times.
✓ Branch 1 taken 16129 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18397 times.
45866 Encryption::is_master_key_encryption(encrypt) &&
4853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 388 times.
776 !DICT_TF2_FLAG_IS_SET(ctx->old_table,
4854 DICT_TF2_ENCRYPTION_FILE_PER_TABLE)) {
4855 dict_mem_table_free(ctx->new_table);
4856 my_error(ER_TABLESPACE_CANNOT_ENCRYPT, MYF(0));
4857 goto new_clustered_failed;
4858
2/2
✓ Branch 0 taken 624 times.
✓ Branch 1 taken 17773 times.
36794 } else if (Encryption::is_master_key_encryption(encrypt)) {
4859 /* Set the encryption flag. */
4860
4861 /* Check if keyring is ready. */
4862
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 624 times.
1248 if (!Encryption::check_keyring()) {
4863 dict_mem_table_free(ctx->new_table);
4864 my_error(ER_CANNOT_FIND_KEY_IN_KEYRING, MYF(0));
4865 goto new_clustered_failed;
4866 } else {
4867 /* This flag will be used to set encryption
4868 option for file-per-table tablespace. */
4869 1248 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
4870 }
4871 }
4872
4873
1/2
✓ Branch 0 taken 30467 times.
✗ Branch 1 not taken.
60934 dict_sys_mutex_exit();
4874
4875 60934 keyring_encryption_key_id.was_encryption_key_id_set =
4876 60934 ha_alter_info->create_info->was_encryption_key_id_set;
4877 60934 keyring_encryption_key_id.id = key_id;
4878
4879 121820 error = row_create_table_for_mysql(ctx->new_table, compression,
4880
1/2
✓ Branch 0 taken 30443 times.
✗ Branch 1 not taken.
60934 ha_alter_info->create_info, ctx->trx, nullptr,
4881 mode, keyring_encryption_key_id);
4882
4883
1/2
✓ Branch 0 taken 30443 times.
✗ Branch 1 not taken.
60886 dict_sys_mutex_enter();
4884
4885
2/7
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
60886 switch (error) {
4886 dict_table_t *temp_table;
4887 60876 case DB_SUCCESS:
4888 /* To bump up the table ref count and move it
4889 to LRU list if it's not temporary table */
4890
2/4
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30438 times.
60876 ut_ad(dict_sys_mutex_own());
4891
3/6
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30438 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30438 times.
✗ Branch 5 not taken.
121752 if (!ctx->new_table->is_temporary() &&
4892
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
60876 !ctx->new_table->explicitly_non_lru) {
4893
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
60876 dict_table_allow_eviction(ctx->new_table);
4894 }
4895
2/2
✓ Branch 0 taken 30315 times.
✓ Branch 1 taken 123 times.
60876 if ((ctx->new_table->flags2 &
4896 60630 (DICT_TF2_FTS | DICT_TF2_FTS_ADD_DOC_ID)) ||
4897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30315 times.
60630 ctx->new_table->fts != nullptr) {
4898
1/2
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
246 fts_freeze_aux_tables(ctx->new_table);
4899 }
4900 temp_table =
4901
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
60876 dd_table_open_on_name_in_mem(ctx->new_table->name.m_name, true);
4902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
60876 ut_a(ctx->new_table == temp_table);
4903 /* n_ref_count must be 1, because purge cannot
4904 be executing on this very table as we are
4905 holding MDL lock. */
4906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30438 times.
60876 assert(ctx->new_table->get_ref_count() == 1);
4907 60876 break;
4908 case DB_TABLESPACE_EXISTS:
4909 my_error(ER_TABLESPACE_EXISTS, MYF(0), new_table_name);
4910 goto new_clustered_failed;
4911 case DB_DUPLICATE_KEY:
4912 my_error(HA_ERR_TABLE_EXIST, MYF(0), altered_table->s->table_name.str);
4913 goto new_clustered_failed;
4914 case DB_UNSUPPORTED:
4915 my_error(ER_UNSUPPORTED_EXTENSION, MYF(0), new_table_name);
4916 goto new_clustered_failed;
4917 case DB_IO_NO_PUNCH_HOLE_FS:
4918 my_error(ER_INNODB_COMPRESSION_FAILURE, MYF(0),
4919 "Punch hole not supported by the filesystem or the tablespace "
4920 "page size is not large enough.");
4921 goto new_clustered_failed;
4922 case DB_IO_NO_PUNCH_HOLE_TABLESPACE:
4923 my_error(ER_INNODB_COMPRESSION_FAILURE, MYF(0),
4924 "Page Compression is not supported for this tablespace");
4925 goto new_clustered_failed;
4926 10 default:
4927
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 my_error_innodb(error, table_name, flags);
4928 10 new_clustered_failed:
4929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 ut_ad(user_table->get_ref_count() == 1);
4930
4931 10 goto err_exit;
4932 }
4933
4934
2/2
✓ Branch 0 taken 9342 times.
✓ Branch 1 taken 21096 times.
60876 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN) {
4935 add_cols =
4936
2/4
✓ Branch 0 taken 9342 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9342 times.
✗ Branch 3 not taken.
18684 dtuple_create_with_vcol(ctx->heap, ctx->new_table->get_n_cols(),
4937
1/2
✓ Branch 0 taken 9342 times.
✗ Branch 1 not taken.
18684 dict_table_get_n_v_cols(ctx->new_table));
4938
4939
1/2
✓ Branch 0 taken 9342 times.
✗ Branch 1 not taken.
18684 dict_table_copy_types(add_cols, ctx->new_table);
4940 } else {
4941 42192 add_cols = nullptr;
4942 }
4943
4944 121752 ctx->col_map = innobase_build_col_map(ha_alter_info, altered_table,
4945
1/2
✓ Branch 0 taken 30438 times.
✗ Branch 1 not taken.
60876 old_table, ctx->new_table, user_table,
4946 add_cols, ctx->heap, prebuilt);
4947 60876 ctx->add_cols = add_cols;
4948 } else {
4949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5291 times.
10582 assert(
4950 !innobase_need_rebuild(ha_alter_info, old_table, is_file_per_table));
4951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5291 times.
10582 assert(old_table->s->primary_key == altered_table->s->primary_key);
4952
4953
3/4
✓ Branch 0 taken 5291 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9630 times.
✓ Branch 3 taken 5286 times.
29832 for (dict_index_t *index = user_table->first_index(); index != nullptr;
4954
1/2
✓ Branch 0 taken 9625 times.
✗ Branch 1 not taken.
19250 index = index->next()) {
4955
7/8
✓ Branch 0 taken 9399 times.
✓ Branch 1 taken 231 times.
✓ Branch 2 taken 9399 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 9394 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 9625 times.
19260 if (!index->to_be_dropped && index->is_corrupted()) {
4956
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
4957 10 goto error_handled;
4958 }
4959 }
4960
4961
6/6
✓ Branch 0 taken 4951 times.
✓ Branch 1 taken 335 times.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 4922 times.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 5257 times.
10572 if (!ctx->new_table->fts && innobase_fulltext_exist(altered_table)) {
4962
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
58 ctx->new_table->fts = fts_create(ctx->new_table);
4963 58 ctx->new_table->fts->doc_col = fts_doc_id_col;
4964 }
4965
4966 /* Check if we need to update mtypes of legacy GIS columns.
4967 This check is only needed when we don't have to rebuild
4968 the table, since rebuild would update all mtypes for GIS
4969 columns */
4970
1/2
✓ Branch 0 taken 5286 times.
✗ Branch 1 not taken.
10572 error = innobase_check_gis_columns(ha_alter_info, ctx->new_table);
4971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5286 times.
10572 if (error != DB_SUCCESS) {
4972 ut_ad(error == DB_ERROR);
4973 error = DB_UNSUPPORTED;
4974 goto error_handling;
4975 }
4976 }
4977
4978
2/4
✓ Branch 0 taken 35724 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35724 times.
71448 ut_ad(!dict_table_is_compressed_temporary(ctx->new_table));
4979
4980 /* Assign table_id, so that no table id of
4981 fts_create_index_tables() will be written to the undo logs. */
4982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35724 times.
71448 assert(ctx->new_table->id != 0);
4983
4984 /* Create the indexes and load into dictionary. */
4985
4986
2/2
✓ Branch 0 taken 45473 times.
✓ Branch 1 taken 35583 times.
162112 for (ulint a = 0; a < ctx->num_to_add_index; a++) {
4987
2/2
✓ Branch 0 taken 317 times.
✓ Branch 1 taken 45156 times.
90946 if (index_defs[a].m_ind_type & DICT_VIRTUAL &&
4988
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 309 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
634 ha_alter_info->virtual_column_drop_count > 0 && !new_clustered) {
4989 16 innodb_v_adjust_idx_col(ha_alter_info, old_table,
4990 16 ha_alter_info->virtual_column_drop_count,
4991
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
16 &index_defs[a]);
4992 }
4993
4994 181664 ctx->add_index[a] =
4995 90946 ddl::create_index(ctx->trx, ctx->new_table, &index_defs[a], add_v);
4996
4997 90718 add_key_nums[a] = index_defs[a].m_key_number;
4998
4999
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 45333 times.
90718 if (!ctx->add_index[a]) {
5000 52 error = ctx->trx->error_state;
5001
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
52 assert(error != DB_SUCCESS);
5002 52 goto error_handling;
5003 }
5004
5005
2/4
✓ Branch 0 taken 45333 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 45333 times.
90666 assert(ctx->add_index[a]->is_committed() == new_clustered);
5006
5007
2/2
✓ Branch 0 taken 459 times.
✓ Branch 1 taken 44874 times.
90666 if (ctx->add_index[a]->type & DICT_FTS) {
5008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 459 times.
918 assert(num_fts_index);
5009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 459 times.
918 assert(!fts_index);
5010
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 459 times.
918 assert(ctx->add_index[a]->type == DICT_FTS);
5011 918 fts_index = ctx->add_index[a];
5012 }
5013
5014 /* If only online ALTER TABLE operations have been
5015 requested, allocate a modification log. If the table
5016 will be locked anyway, the modification
5017 log is unnecessary. When rebuilding the table
5018 (new_clustered), we will allocate the log for the
5019 clustered index of the old table, later. */
5020
9/10
✓ Branch 0 taken 5439 times.
✓ Branch 1 taken 39894 times.
✓ Branch 2 taken 4827 times.
✓ Branch 3 taken 612 times.
✓ Branch 4 taken 4823 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4823 times.
✓ Branch 8 taken 40510 times.
✓ Branch 9 taken 4823 times.
100312 if (new_clustered || !ctx->online || user_table->ibd_file_missing ||
5021 9646 dict_table_is_discarded(user_table)) {
5022 /* No need to allocate a modification log. */
5023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40510 times.
81020 ut_ad(!ctx->add_index[a]->online_log);
5024
1/2
✓ Branch 0 taken 4823 times.
✗ Branch 1 not taken.
9646 } else if (ctx->add_index[a]->type & DICT_FTS) {
5025 /* Fulltext indexes are not covered
5026 by a modification log. */
5027 } else {
5028
3/4
✓ Branch 0 taken 4823 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4822 times.
9646 DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter",
5029 error = DB_OUT_OF_MEMORY;
5030 goto error_handling;);
5031
1/2
✓ Branch 0 taken 4822 times.
✗ Branch 1 not taken.
9644 rw_lock_x_lock(&ctx->add_index[a]->lock, UT_LOCATION_HERE);
5032
1/2
✓ Branch 0 taken 4822 times.
✗ Branch 1 not taken.
9644 bool ok = row_log_allocate(ctx->add_index[a], nullptr, true, nullptr,
5033 nullptr, path);
5034
1/2
✓ Branch 0 taken 4822 times.
✗ Branch 1 not taken.
9644 rw_lock_x_unlock(&ctx->add_index[a]->lock);
5035
5036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4822 times.
9644 if (!ok) {
5037 error = DB_OUT_OF_MEMORY;
5038 goto error_handling;
5039 }
5040 }
5041 }
5042
5043
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35583 times.
71166 ut_ad(new_clustered == ctx->need_rebuild());
5044
5045
3/4
✓ Branch 0 taken 35583 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35582 times.
71166 DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter", error = DB_OUT_OF_MEMORY;
5046 goto error_handling;);
5047
5048
2/2
✓ Branch 0 taken 30364 times.
✓ Branch 1 taken 5218 times.
71164 if (new_clustered) {
5049
1/2
✓ Branch 0 taken 30364 times.
✗ Branch 1 not taken.
60728 dict_index_t *clust_index = user_table->first_index();
5050
1/2
✓ Branch 0 taken 30364 times.
✗ Branch 1 not taken.
60728 dict_index_t *new_clust_index = ctx->new_table->first_index();
5051 60728 ctx->skip_pk_sort =
5052
1/2
✓ Branch 0 taken 30364 times.
✗ Branch 1 not taken.
60728 innobase_pk_order_preserved(ctx->col_map, clust_index, new_clust_index);
5053
5054
4/6
✓ Branch 0 taken 30364 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 30320 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 44 times.
60728 DBUG_EXECUTE_IF("innodb_alter_table_pk_assert_no_sort",
5055 assert(ctx->skip_pk_sort););
5056
5057
2/2
✓ Branch 0 taken 30072 times.
✓ Branch 1 taken 292 times.
60728 if (ctx->online) {
5058 /* Allocate a log for online table rebuild. */
5059
1/2
✓ Branch 0 taken 30072 times.
✗ Branch 1 not taken.
60144 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
5060 120288 bool ok = row_log_allocate(
5061 clust_index, ctx->new_table,
5062
1/2
✓ Branch 0 taken 30072 times.
✗ Branch 1 not taken.
60144 !(ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX),
5063 ctx->add_cols, ctx->col_map, path);
5064
1/2
✓ Branch 0 taken 30072 times.
✗ Branch 1 not taken.
60144 rw_lock_x_unlock(&clust_index->lock);
5065
5066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30072 times.
60144 if (!ok) {
5067 error = DB_OUT_OF_MEMORY;
5068 goto error_handling;
5069 }
5070 }
5071 }
5072
5073
2/2
✓ Branch 0 taken 34699 times.
✓ Branch 1 taken 883 times.
71164 if (ctx->online) {
5074 /* Assign a consistent read view for the index build scan. */
5075
1/2
✓ Branch 0 taken 34699 times.
✗ Branch 1 not taken.
69398 trx_assign_read_view(ctx->prebuilt->trx);
5076 }
5077
5078
2/2
✓ Branch 0 taken 459 times.
✓ Branch 1 taken 35123 times.
71164 if (fts_index) {
5079 /* Ensure that the dictionary operation mode will
5080 not change while creating the auxiliary tables. */
5081 #ifdef UNIV_DEBUG
5082
1/2
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
918 trx_dict_op_t op = trx_get_dict_operation(ctx->trx);
5083 #endif
5084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 459 times.
918 ut_ad(ctx->trx->dict_operation_lock_mode == RW_X_LATCH);
5085
2/4
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 459 times.
918 ut_ad(dict_sys_mutex_own());
5086
2/4
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 459 times.
918 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5087
5088 918 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
5089
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 336 times.
918 if (new_clustered) {
5090 /* For !new_clustered, this will be set at
5091 commit_cache_norebuild(). */
5092 492 ctx->new_table->fts_doc_id_index =
5093
1/2
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
246 dict_table_get_index_on_name(ctx->new_table, FTS_DOC_ID_INDEX_NAME);
5094
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
246 assert(ctx->new_table->fts_doc_id_index != nullptr);
5095 }
5096
5097 /* This function will commit the transaction and reset
5098 the trx_t::dict_operation flag on success. */
5099
5100
1/2
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
918 dict_sys_mutex_exit();
5101
1/2
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
918 error = fts_create_index_tables(ctx->trx, fts_index);
5102
1/2
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
918 dict_sys_mutex_enter();
5103
5104
2/4
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 459 times.
918 DBUG_EXECUTE_IF("innodb_test_fail_after_fts_index_table",
5105 error = DB_LOCK_WAIT_TIMEOUT;
5106 goto error_handling;);
5107
5108
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 455 times.
918 if (error != DB_SUCCESS) {
5109 8 goto error_handling;
5110 }
5111
5112
5/6
✓ Branch 0 taken 455 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 190 times.
✓ Branch 3 taken 265 times.
✓ Branch 4 taken 190 times.
✓ Branch 5 taken 265 times.
1820 if (!ctx->new_table->fts ||
5113 910 ib_vector_size(ctx->new_table->fts->indexes) == 0) {
5114 bool exist_fts_common;
5115
5116
1/2
✓ Branch 0 taken 190 times.
✗ Branch 1 not taken.
380 dict_sys_mutex_exit();
5117
1/2
✓ Branch 0 taken 190 times.
✗ Branch 1 not taken.
380 exist_fts_common = fts_check_common_tables_exist(ctx->new_table);
5118
5119
2/2
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 40 times.
380 if (!exist_fts_common) {
5120 600 error = fts_create_common_tables(ctx->trx, ctx->new_table,
5121
1/2
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
300 user_table->name.m_name, true);
5122
5123
2/4
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 150 times.
300 DBUG_EXECUTE_IF("innodb_test_fail_after_fts_common_table",
5124 error = DB_LOCK_WAIT_TIMEOUT;);
5125
5126
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 148 times.
300 if (error != DB_SUCCESS) {
5127
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 dict_sys_mutex_enter();
5128 4 goto error_handling;
5129 }
5130
5131 296 build_fts_common = true;
5132 }
5133
5134 1128 error = innobase_fts_load_stopword(ctx->new_table, nullptr,
5135
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
376 ctx->prebuilt->trx->mysql_thd)
5136
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
376 ? DB_SUCCESS
5137 : DB_ERROR;
5138
5139
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
376 dict_sys_mutex_enter();
5140
5141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
376 if (error != DB_SUCCESS) {
5142 goto error_handling;
5143 }
5144 }
5145
5146
2/4
✓ Branch 0 taken 453 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 453 times.
906 ut_ad(trx_get_dict_operation(ctx->trx) == op);
5147 }
5148
5149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35576 times.
71152 assert(error == DB_SUCCESS);
5150
5151
4/4
✓ Branch 0 taken 35428 times.
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 305 times.
✓ Branch 3 taken 35123 times.
71152 if (build_fts_common || fts_index) {
5152
1/2
✓ Branch 0 taken 453 times.
✗ Branch 1 not taken.
906 fts_freeze_aux_tables(ctx->new_table);
5153 }
5154
5155
1/2
✓ Branch 0 taken 35576 times.
✗ Branch 1 not taken.
71152 row_mysql_unlock_data_dictionary(ctx->prebuilt->trx);
5156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35576 times.
71152 ut_ad(ctx->trx == ctx->prebuilt->trx);
5157 71152 dict_locked = false;
5158
5159
2/4
✓ Branch 0 taken 35576 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35576 times.
71152 if (dd_prepare_inplace_alter_table(ctx->prebuilt->trx->mysql_thd, user_table,
5160 ctx->new_table, old_dd_tab)) {
5161 error = DB_ERROR;
5162 }
5163
5164
1/2
✓ Branch 0 taken 35576 times.
✗ Branch 1 not taken.
71152 if (error == DB_SUCCESS) {
5165
2/2
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 35428 times.
71152 if (build_fts_common) {
5166
2/4
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 148 times.
296 if (!fts_create_common_dd_tables(ctx->new_table)) {
5167 error = DB_ERROR;
5168 goto error_handling;
5169 }
5170 }
5171
5172
2/2
✓ Branch 0 taken 453 times.
✓ Branch 1 taken 35123 times.
71152 if (fts_index) {
5173
1/2
✓ Branch 0 taken 453 times.
✗ Branch 1 not taken.
906 error = fts_create_index_dd_tables(ctx->new_table);
5174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 453 times.
906 if (error != DB_SUCCESS) {
5175 goto error_handling;
5176 }
5177 }
5178 }
5179
5180
4/6
✓ Branch 0 taken 35576 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35575 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
71152 DBUG_EXECUTE_IF("crash_innodb_add_index_after", DBUG_SUICIDE(););
5181
5182 71150 error_handling:
5183
5184
4/4
✓ Branch 0 taken 35461 times.
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 311 times.
✓ Branch 3 taken 35150 times.
71218 if (build_fts_common || fts_index) {
5185
1/2
✓ Branch 0 taken 459 times.
✗ Branch 1 not taken.
918 fts_detach_aux_tables(ctx->new_table, dict_locked);
5186 }
5187
5188 /* After an error, remove all those index definitions from the
5189 dictionary which were defined. */
5190
5191
2/5
✓ Branch 0 taken 35575 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
71218 switch (error) {
5192 71150 case DB_SUCCESS:
5193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35575 times.
71150 ut_a(!dict_locked);
5194
5195
1/2
✓ Branch 0 taken 35575 times.
✗ Branch 1 not taken.
71150 ut_d(dict_sys_mutex_enter());
5196
1/2
✓ Branch 0 taken 35575 times.
✗ Branch 1 not taken.
71150 ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_PARTIAL_OK));
5197
1/2
✓ Branch 0 taken 35575 times.
✗ Branch 1 not taken.
71150 ut_d(dict_sys_mutex_exit());
5198 71150 return false;
5199 case DB_TABLESPACE_EXISTS:
5200 my_error(ER_TABLESPACE_EXISTS, MYF(0), "(unknown)");
5201 break;
5202 case DB_DUPLICATE_KEY:
5203 my_error(ER_DUP_KEY, MYF(0));
5204 break;
5205 case DB_UNSUPPORTED:
5206 my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
5207 break;
5208 68 default:
5209
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
68 my_error_innodb(error, table_name, user_table->flags);
5210 }
5211
5212 82 error_handled:
5213
5214 82 ctx->prebuilt->trx->error_index = nullptr;
5215 82 ctx->trx->error_state = DB_SUCCESS;
5216
5217
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 39 times.
82 if (!dict_locked) {
5218
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 row_mysql_lock_data_dictionary(ctx->prebuilt->trx, UT_LOCATION_HERE);
5219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 ut_ad(ctx->trx == ctx->prebuilt->trx);
5220 }
5221
5222
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 32 times.
82 if (new_clustered) {
5223
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 if (ctx->need_rebuild()) {
5224
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
18 if (DICT_TF2_FLAG_IS_SET(ctx->new_table, DICT_TF2_FTS)) {
5225
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 innobase_drop_fts_index_table(ctx->new_table, ctx->trx);
5226 }
5227
5228
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 dict_table_close_and_drop(ctx->trx, ctx->new_table);
5229
5230 /* Free the log for online table rebuild, if
5231 one was allocated. */
5232
5233
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 dict_index_t *clust_index = user_table->first_index();
5234
5235
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
5236
5237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
18 if (clust_index->online_log) {
5238 ut_ad(ctx->online);
5239 row_log_free(clust_index->online_log);
5240 clust_index->online_status = ONLINE_INDEX_COMPLETE;
5241 }
5242
5243
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 rw_lock_x_unlock(&clust_index->lock);
5244 }
5245
5246 /* n_ref_count must be 1, because purge cannot
5247 be executing on this very table as we are
5248 holding MDL. */
5249
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 assert(user_table->get_ref_count() == 1 || ctx->online);
5250 } else {
5251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
64 ut_ad(!ctx->need_rebuild());
5252 64 ddl::drop_indexes(ctx->trx, user_table, true);
5253 }
5254
5255
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
82 ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_ALL_COMPLETE));
5256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
82 ut_ad(!user_table->drop_aborted);
5257
5258 82 err_exit:
5259 #ifdef UNIV_DEBUG
5260 /* Clear the to_be_dropped flag in the data dictionary cache. */
5261
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 46 times.
96 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
5262
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 assert(ctx->drop_index[i]->is_committed());
5263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 assert(ctx->drop_index[i]->to_be_dropped);
5264 4 ctx->drop_index[i]->to_be_dropped = 0;
5265 }
5266 #endif /* UNIV_DEBUG */
5267
5268
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
92 row_mysql_unlock_data_dictionary(ctx->prebuilt->trx);
5269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
92 ut_ad(ctx->trx == ctx->prebuilt->trx);
5270
5271 92 destroy(ctx);
5272 92 ha_alter_info->handler_ctx = nullptr;
5273
5274 92 return true;
5275 71246 }
5276
5277 /* Check whether an index is needed for the foreign key constraint.
5278 If so, if it is dropped, is there an equivalent index can play its role.
5279 @return true if the index is needed and can't be dropped */
5280 1992 [[nodiscard]] static bool innobase_check_foreign_key_index(
5281 Alter_inplace_info *ha_alter_info, /*!< in: Structure describing
5282 changes to be done by ALTER
5283 TABLE */
5284 dict_index_t *index, /*!< in: index to check */
5285 dict_table_t *indexed_table, /*!< in: table that owns the
5286 foreign keys */
5287 const char **col_names, /*!< in: column names, or NULL
5288 for indexed_table->col_names */
5289 trx_t *trx, /*!< in/out: transaction */
5290 dict_foreign_t **drop_fk, /*!< in: Foreign key constraints
5291 to drop */
5292 ulint n_drop_fk) /*!< in: Number of foreign keys
5293 to drop */
5294 {
5295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1992 times.
1992 ut_ad(index != nullptr);
5296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1992 times.
1992 ut_ad(indexed_table != nullptr);
5297
5298 1992 const dict_foreign_set *fks = &indexed_table->referenced_set;
5299
5300 /* Check for all FK references from other tables to the index. */
5301
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1992 times.
2015 for (dict_foreign_set::const_iterator it = fks->begin(); it != fks->end();
5302 23 ++it) {
5303 23 dict_foreign_t *foreign = *it;
5304
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if (foreign->referenced_index != index) {
5305 23 continue;
5306 }
5307 ut_ad(indexed_table == foreign->referenced_table);
5308
5309 if (nullptr == dict_foreign_find_index(indexed_table, col_names,
5310 foreign->referenced_col_names,
5311 foreign->n_fields, index,
5312 /*check_charsets=*/true,
5313 /*check_null=*/false) &&
5314 nullptr == innobase_find_equiv_index(foreign->referenced_col_names,
5315 foreign->n_fields,
5316 ha_alter_info->key_info_buffer,
5317 ha_alter_info->index_add_buffer,
5318 ha_alter_info->index_add_count)) {
5319 /* Index cannot be dropped. */
5320 trx->error_index = index;
5321 return (true);
5322 }
5323 }
5324
5325 1992 fks = &indexed_table->foreign_set;
5326
5327 /* Check for all FK references in current table using the index. */
5328
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1992 times.
2022 for (dict_foreign_set::const_iterator it = fks->begin(); it != fks->end();
5329 30 ++it) {
5330 30 dict_foreign_t *foreign = *it;
5331
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 14 times.
30 if (foreign->foreign_index != index) {
5332 16 continue;
5333 }
5334
5335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 ut_ad(indexed_table == foreign->foreign_table);
5336
5337 14 if (!innobase_dropping_foreign(foreign, drop_fk, n_drop_fk) &&
5338
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
12 nullptr == dict_foreign_find_index(indexed_table, col_names,
5339 foreign->foreign_col_names,
5340
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 foreign->n_fields, index,
5341 /*check_charsets=*/true,
5342
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
26 /*check_null=*/false) &&
5343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 nullptr == innobase_find_equiv_index(foreign->foreign_col_names,
5344 11 foreign->n_fields,
5345 11 ha_alter_info->key_info_buffer,
5346
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 ha_alter_info->index_add_buffer,
5347 ha_alter_info->index_add_count)) {
5348 /* Index cannot be dropped. */
5349 trx->error_index = index;
5350 return (true);
5351 }
5352 }
5353
5354 1992 return (false);
5355 }
5356
5357 /** Rename a given index in the InnoDB data dictionary cache.
5358 @param[in,out] index index to rename
5359 @param new_name new index name */
5360 78 static void rename_index_in_cache(dict_index_t *index, const char *new_name) {
5361
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 DBUG_TRACE;
5362
5363
2/4
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
78 ut_ad(dict_sys_mutex_own());
5364
2/4
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
78 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5365
5366 78 size_t old_name_len = strlen(index->name);
5367 78 size_t new_name_len = strlen(new_name);
5368
5369
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 46 times.
78 if (old_name_len >= new_name_len) {
5370 /* reuse the old buffer for the name if it is large enough */
5371 32 memcpy(const_cast<char *>(index->name()), new_name, new_name_len + 1);
5372 } else {
5373 /* Free the old chunk of memory if it is at the topmost
5374 place in the heap, otherwise the old chunk will be freed
5375 when the index is evicted from the cache. This code will
5376 kick-in in a repeated ALTER sequences where the old name is
5377 alternately longer/shorter than the new name:
5378 1. ALTER TABLE t RENAME INDEX a TO aa;
5379 2. ALTER TABLE t RENAME INDEX aa TO a;
5380 3. go to 1. */
5381 index->name =
5382
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 mem_heap_strdup_replace(index->heap,
5383 /* Presumed topmost element of the heap: */
5384 46 index->name, old_name_len + 1, new_name);
5385 }
5386 78 }
5387
5388 /**
5389 Rename all indexes in data dictionary cache of a given table that are
5390 specified in ha_alter_info.
5391
5392 @param ctx alter context, used to fetch the list of indexes to rename
5393 @param ha_alter_info fetch the new names from here
5394 */
5395 17016 static void rename_indexes_in_cache(const ha_innobase_inplace_ctx *ctx,
5396 const Alter_inplace_info *ha_alter_info) {
5397
1/2
✓ Branch 0 taken 17016 times.
✗ Branch 1 not taken.
17016 DBUG_TRACE;
5398
5399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17016 times.
17016 ut_ad(ctx->num_to_rename == ha_alter_info->index_rename_count);
5400
5401
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 17016 times.
17094 for (ulint i = 0; i < ctx->num_to_rename; i++) {
5402 78 KEY_PAIR *pair = &ha_alter_info->index_rename_buffer[i];
5403 dict_index_t *index;
5404
5405 78 index = ctx->rename[i];
5406
5407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 ut_ad(strcmp(index->name, pair->old_key->name) == 0);
5408
5409
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 rename_index_in_cache(index, pair->new_key->name);
5410 }
5411 17016 }
5412
5413 /** Fill the stored column information in s_cols list.
5414 @param[in] altered_table mysql table object
5415 @param[in] table innodb table object
5416 @param[out] s_cols list of stored column
5417 @param[out] s_heap heap for storing stored
5418 column information. */
5419 94 static void alter_fill_stored_column(const TABLE *altered_table,
5420 dict_table_t *table,
5421 dict_s_col_list **s_cols,
5422 mem_heap_t **s_heap) {
5423 94 ulint n_cols = altered_table->s->fields;
5424 94 ulint stored_col_no = 0;
5425
5426
2/2
✓ Branch 0 taken 287 times.
✓ Branch 1 taken 94 times.
381 for (ulint i = 0; i < n_cols; i++) {
5427 287 Field *field = altered_table->field[i];
5428 dict_s_col_t s_col;
5429
5430
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 273 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10 times.
287 if (!innobase_is_v_fld(field)) {
5431 277 stored_col_no++;
5432 }
5433
5434
2/2
✓ Branch 0 taken 283 times.
✓ Branch 1 taken 4 times.
287 if (!innobase_is_s_fld(field)) {
5435 283 continue;
5436 }
5437
5438 4 ulint num_base = field->gcol_info->non_virtual_base_columns();
5439
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 dict_col_t *col = table->get_col(stored_col_no);
5440
5441 4 s_col.m_col = col;
5442 4 s_col.s_pos = i;
5443
5444
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (*s_cols == nullptr) {
5445
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 *s_cols = ut::new_withkey<dict_s_col_list>(UT_NEW_THIS_FILE_PSI_KEY);
5446
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 *s_heap = mem_heap_create(100, UT_LOCATION_HERE);
5447 }
5448
5449
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (num_base != 0) {
5450 1 s_col.base_col = static_cast<dict_col_t **>(
5451
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 mem_heap_zalloc(*s_heap, num_base * sizeof(dict_col_t)));
5452 } else {
5453 3 s_col.base_col = nullptr;
5454 }
5455
5456 4 s_col.num_base = num_base;
5457
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 innodb_base_col_setup_for_stored(table, field, &s_col);
5458
5459
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 (*s_cols)->push_back(s_col);
5460 }
5461 94 }
5462
5463 template <typename Table>
5464 32 void static adjust_row_format(TABLE *old_table, TABLE *altered_table,
5465 const Table *old_dd_tab, Table *new_dd_tab) {
5466
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
32 ut_ad(old_table->s->row_type == ROW_TYPE_DEFAULT ||
5467 old_table->s->row_type == ROW_TYPE_COMPRESSED);
5468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_table->s->row_type == altered_table->s->row_type);
5469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_table->s->real_row_type != altered_table->s->real_row_type);
5470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_dd_tab->table().row_format() != new_dd_tab->table().row_format());
5471
5472 /* Revert the row_format in DD for altered table */
5473 32 new_dd_tab->table().set_row_format(old_dd_tab->table().row_format());
5474
5475 /* Revert the real_row_format in table share for altered table */
5476
3/5
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
32 switch (old_dd_tab->table().row_format()) {
5477 24 case dd::Table::RF_REDUNDANT:
5478 24 altered_table->s->real_row_type = ROW_TYPE_REDUNDANT;
5479 24 break;
5480 6 case dd::Table::RF_COMPACT:
5481 6 altered_table->s->real_row_type = ROW_TYPE_COMPACT;
5482 6 break;
5483 2 case dd::Table::RF_COMPRESSED:
5484 2 altered_table->s->real_row_type = ROW_TYPE_COMPRESSED;
5485 2 break;
5486 case dd::Table::RF_DYNAMIC:
5487 altered_table->s->real_row_type = ROW_TYPE_DYNAMIC;
5488 break;
5489 default:
5490 ut_d(ut_error);
5491 }
5492 }
5493
5494 /** Implementation of prepare_inplace_alter_table()
5495 @tparam Table dd::Table or dd::Partition
5496 @param[in] altered_table TABLE object for new version of table.
5497 @param[in,out] ha_alter_info Structure describing changes to be done
5498 by ALTER TABLE and holding data used
5499 during in-place alter.
5500 @param[in] old_dd_tab dd::Table object representing old
5501 version of the table
5502 @param[in,out] new_dd_tab dd::Table object representing new
5503 version of the table
5504 @retval true Failure
5505 @retval false Success */
5506 template <typename Table>
5507 113006 bool ha_innobase::prepare_inplace_alter_table_impl(
5508 TABLE *altered_table, Alter_inplace_info *ha_alter_info,
5509 const Table *old_dd_tab, Table *new_dd_tab) {
5510 113006 dict_index_t **drop_index = nullptr; /*!< Index to be dropped */
5511 ulint n_drop_index; /*!< Number of indexes to drop */
5512 dict_index_t **rename_index; /*!< Indexes to be dropped */
5513 ulint n_rename_index; /*!< Number of indexes to rename */
5514 dict_foreign_t **drop_fk; /*!< Foreign key constraints to drop */
5515 ulint n_drop_fk; /*!< Number of foreign keys to drop */
5516 113006 dict_foreign_t **add_fk = nullptr; /*!< Foreign key constraints to drop */
5517 ulint n_add_fk; /*!< Number of foreign keys to drop */
5518 dict_table_t *indexed_table; /*!< Table where indexes are created */
5519 mem_heap_t *heap;
5520 const char **col_names;
5521 int error;
5522 ulint max_col_len;
5523 113006 ulint add_autoinc_col_no = ULINT_UNDEFINED;
5524 113006 ulonglong autoinc_col_max_value = 0;
5525 113006 ulint fts_doc_col_no = ULINT_UNDEFINED;
5526 113006 bool add_fts_doc_id = false;
5527 113006 bool add_fts_doc_id_idx = false;
5528 113006 bool add_fts_idx = false;
5529 113006 dict_s_col_list *s_cols = nullptr;
5530 113006 mem_heap_t *s_heap = nullptr;
5531 113006 ulint encrypt_flag = 0;
5532
5533
1/2
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
113006 DBUG_TRACE;
5534
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56503 times.
113006 assert(!ha_alter_info->handler_ctx);
5535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56503 times.
113006 assert(ha_alter_info->create_info);
5536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56503 times.
113006 assert(!srv_read_only_mode);
5537
5538 113006 MONITOR_ATOMIC_INC(MONITOR_PENDING_ALTER_TABLE);
5539
5540 #ifdef UNIV_DEBUG
5541
3/4
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75468 times.
✓ Branch 3 taken 56503 times.
263942 for (dict_index_t *index = m_prebuilt->table->first_index(); index;
5542
1/2
✓ Branch 0 taken 75468 times.
✗ Branch 1 not taken.
150936 index = index->next()) {
5543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75468 times.
150936 ut_ad(!index->to_be_dropped);
5544 }
5545 #endif /* UNIV_DEBUG */
5546
5547
1/2
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
113006 ut_d(dict_sys_mutex_enter());
5548
1/2
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
113006 ut_d(dict_table_check_for_dup_indexes(m_prebuilt->table, CHECK_ABORTED_OK));
5549
1/2
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
113006 ut_d(dict_sys_mutex_exit());
5550
5551 113006 indexed_table = m_prebuilt->table;
5552
5553
3/4
✓ Branch 0 taken 56503 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 56502 times.
113006 if (indexed_table->is_corrupted()) {
5554 /* The clustered index is corrupted. */
5555
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
5556 2 return true;
5557 }
5558
5559
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 56473 times.
113004 if (dict_table_is_discarded(indexed_table)) {
5560 116 Instant_Type type = innobase_support_instant(
5561
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
58 ha_alter_info, m_prebuilt->table, this->table, altered_table);
5562 /* Even if some operations can be done instantly without rebuilding, they
5563 are still disallowed to behave like before. */
5564 116 if (innobase_need_rebuild(
5565 58 ha_alter_info, table,
5566
6/8
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 19 times.
96 dict_table_is_file_per_table(m_prebuilt->table)) ||
5567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
38 (type == Instant_Type::INSTANT_VIRTUAL_ONLY ||
5568 type == Instant_Type::INSTANT_ADD_DROP_COLUMN)) {
5569
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
20 my_error(ER_TABLESPACE_DISCARDED, MYF(0), indexed_table->name.m_name);
5570 20 return true;
5571 }
5572 }
5573
5574
2/2
✓ Branch 0 taken 6658 times.
✓ Branch 1 taken 49834 times.
112984 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
5575 /* Nothing to do. Since there is no MDL protected, don't
5576 try to drop aborted indexes here. */
5577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6658 times.
13316 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
5578 13316 return false;
5579 }
5580
5581
2/2
✓ Branch 0 taken 1944 times.
✓ Branch 1 taken 47890 times.
99668 if (is_instant(ha_alter_info)) {
5582 7776 Instant_Type type = innobase_support_instant(ha_alter_info, indexed_table,
5583
1/2
✓ Branch 0 taken 1944 times.
✗ Branch 1 not taken.
3888 this->table, altered_table);
5584
5585
2/2
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 216 times.
3888 if (type == Instant_Type::INSTANT_ADD_DROP_COLUMN) {
5586
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1728 times.
3456 ut_a(indexed_table->current_row_version < MAX_ROW_VERSION);
5587 }
5588
5589 3888 return false;
5590 }
5591
5592 /* ALTER TABLE will not implicitly move a table from a single-table
5593 tablespace to the system tablespace when innodb_file_per_table=OFF.
5594 But it will implicitly move a table from the system tablespace to a
5595 single-table tablespace if innodb_file_per_table = ON.
5596 Tables found in a general tablespace will stay there unless ALTER
5597 TABLE contains another TABLESPACE=name. If that is found it will
5598 explicitly move a table to the named tablespace.
5599 So if you specify TABLESPACE=`innodb_system` a table can be moved
5600 into the system tablespace from either a general or file-per-table
5601 tablespace. But from then on, it is labeled as using a shared space
5602 (the create options have tablespace=='innodb_system' and the
5603 SHARED_SPACE flag is set in the table flags) so it can no longer be
5604 implicitly moved to a file-per-table tablespace. */
5605
1/2
✓ Branch 0 taken 47890 times.
✗ Branch 1 not taken.
95780 bool in_system_space = fsp_is_system_or_temp_tablespace(indexed_table->space);
5606 95780 bool is_file_per_table =
5607
4/4
✓ Branch 0 taken 47720 times.
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 33051 times.
✓ Branch 3 taken 14669 times.
95780 !in_system_space && !DICT_TF_HAS_SHARED_SPACE(indexed_table->flags);
5608 #ifdef UNIV_DEBUG
5609 95780 bool in_general_space =
5610
4/4
✓ Branch 0 taken 47720 times.
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 14669 times.
✓ Branch 3 taken 33051 times.
95780 !in_system_space && DICT_TF_HAS_SHARED_SPACE(indexed_table->flags);
5611
5612 /* The table being altered can only be in a system tablespace,
5613 or its own file-per-table tablespace, or a general tablespace. */
5614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47890 times.
95780 ut_ad(1 == in_system_space + is_file_per_table + in_general_space);
5615 #endif /* UNIV_DEBUG */
5616
5617 /* If server has passed a changed row format in the new table definition and
5618 the table isn't going to be rebuilt, revert that row_format change because it
5619 is an implicit change to the previously selected default row format. We want
5620 to keep the table using the original default row_format. */
5621
11/15
✓ Branch 0 taken 3244 times.
✓ Branch 1 taken 44646 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3244 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 44646 times.
✓ Branch 6 taken 3244 times.
✓ Branch 7 taken 157 times.
✓ Branch 8 taken 44489 times.
✓ Branch 9 taken 3260 times.
✓ Branch 10 taken 44630 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3244 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3244 times.
96094 if (old_dd_tab->table().row_format() != new_dd_tab->table().row_format() &&
5622
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 141 times.
314 !innobase_need_rebuild(ha_alter_info, altered_table,
5623
1/2
✓ Branch 0 taken 157 times.
✗ Branch 1 not taken.
314 dict_table_is_file_per_table(m_prebuilt->table))) {
5624
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
32 adjust_row_format(this->table, altered_table, old_dd_tab, new_dd_tab);
5625 }
5626
5627 /* Make a copy for existing tablespace name */
5628 95780 char tablespace[NAME_LEN] = {'\0'};
5629
2/2
✓ Branch 0 taken 14752 times.
✓ Branch 1 taken 33138 times.
95780 if (indexed_table->tablespace) {
5630 29504 strcpy(tablespace, indexed_table->tablespace());
5631 }
5632
5633 185072 adjust_encryption_key_id(ha_alter_info->create_info,
5634
1/2
✓ Branch 0 taken 47890 times.
✗ Branch 1 not taken.
95780 &(new_dd_tab->options()));
5635
5636
3/4
✓ Branch 0 taken 14752 times.
✓ Branch 1 taken 33138 times.
✓ Branch 2 taken 47890 times.
✗ Branch 3 not taken.
95780 create_table_info_t info(m_user_thd, altered_table,
5637 ha_alter_info->create_info, nullptr, nullptr,
5638 95780 indexed_table->tablespace ? tablespace : nullptr,
5639 is_file_per_table, false, 0, 0, false);
5640
5641
1/2
✓ Branch 0 taken 47890 times.
✗ Branch 1 not taken.
95780 info.set_tablespace_type(is_file_per_table);
5642
5643
5/6
✓ Branch 0 taken 22158 times.
✓ Branch 1 taken 25732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22158 times.
✓ Branch 4 taken 25732 times.
✓ Branch 5 taken 22158 times.
140096 if (ha_alter_info->handler_flags & Alter_inplace_info::CHANGE_CREATE_OPTION ||
5644 44316 (Encryption::should_be_keyring_encrypted(
5645 44316 ha_alter_info->create_info->explicit_encryption,
5646
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
44316 ha_alter_info->create_info->encrypt_type.str) &&
5647 innobase_spatial_exist(
5648 altered_table))) { // We need to make sure spatial index was not
5649 // added if this is to be keyring encrypted
5650
1/2
✓ Branch 0 taken 25732 times.
✗ Branch 1 not taken.
51464 const char *invalid_opt = info.create_options_are_invalid();
5651
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 25558 times.
51464 if (invalid_opt != nullptr) {
5652
2/4
✓ Branch 0 taken 174 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 174 times.
✗ Branch 3 not taken.
348 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), table_type(), invalid_opt);
5653 348 goto err_exit_no_heap;
5654 }
5655 }
5656
5657 /* Check if any index name is reserved. */
5658
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47715 times.
95432 if (innobase_index_name_is_reserved(m_user_thd,
5659 95432 ha_alter_info->key_info_buffer,
5660
1/2
✓ Branch 0 taken 47716 times.
✗ Branch 1 not taken.
95432 ha_alter_info->key_count)) {
5661 2 err_exit_no_heap:
5662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
376 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
5663
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
376 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
5664
1/2
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
376 online_retry_drop_dict_indexes(m_prebuilt->table, false);
5665 }
5666 376 return true;
5667 }
5668
5669 95430 indexed_table = m_prebuilt->table;
5670
5671 /* Check that index keys are sensible */
5672
1/2
✓ Branch 0 taken 47715 times.
✗ Branch 1 not taken.
95430 error = innobase_check_index_keys(ha_alter_info, indexed_table);
5673
5674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47715 times.
95430 if (error) {
5675 goto err_exit_no_heap;
5676 }
5677
5678 /* Prohibit renaming a column to something that the table
5679 already contains. */
5680
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 47649 times.
95430 if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME) {
5681
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 64 times.
132 if (!ok_to_rename_column(ha_alter_info, table, altered_table,
5682
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
132 m_prebuilt->table, false, true)) {
5683 4 goto err_exit_no_heap;
5684 }
5685 }
5686
5687
3/4
✓ Branch 0 taken 47713 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 47711 times.
95426 if (!info.innobase_table_flags()) {
5688 4 goto err_exit_no_heap;
5689 }
5690
5691 /* create_table_info_t::innobase_table_flags does not set encryption
5692 flags. There are places where it is done afterwards, there are places
5693 where it isn't done. We need to inspect all code paths and check if
5694 encryption flag can be set in one place. */
5695
2/2
✓ Branch 0 taken 934 times.
✓ Branch 1 taken 46777 times.
95422 if (Encryption::is_master_key_encryption(
5696 95422 ha_alter_info->create_info->encrypt_type.str)) {
5697 /* Set the encryption flag. */
5698 1868 byte *master_key = nullptr;
5699 uint32_t master_key_id;
5700
5701 /* Check if keyring is ready. */
5702 1868 Encryption::get_master_key(&master_key_id, &master_key);
5703
5704
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 934 times.
1868 if (master_key == nullptr) {
5705 goto err_exit_no_heap;
5706 } else {
5707
1/2
✓ Branch 0 taken 934 times.
✗ Branch 1 not taken.
1868 my_free(master_key);
5708 1868 encrypt_flag = DICT_TF2_ENCRYPTION_FILE_PER_TABLE;
5709 }
5710 }
5711
5712 95422 max_col_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(info.flags());
5713
5714 /* Check each index's column length to make sure they do not
5715 exceed limit */
5716
2/2
✓ Branch 0 taken 7811 times.
✓ Branch 1 taken 47707 times.
111036 for (ulint i = 0; i < ha_alter_info->index_add_count; i++) {
5717 15622 const KEY *key =
5718 15622 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
5719
5720
2/2
✓ Branch 0 taken 470 times.
✓ Branch 1 taken 7341 times.
15622 if (key->flags & HA_FULLTEXT) {
5721 /* The column length does not matter for
5722 fulltext search indexes. But, UNIQUE
5723 fulltext indexes are not supported. */
5724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 470 times.
940 assert(!(key->flags & HA_NOSAME));
5725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 470 times.
940 assert(!(key->flags & HA_KEYFLAG_MASK &
5726 ~(HA_FULLTEXT | HA_PACK_KEY | HA_BINARY_PACK_KEY)));
5727 940 add_fts_idx = true;
5728 940 continue;
5729 }
5730
5731
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7337 times.
14682 if (innobase_check_column_length(max_col_len, key)) {
5732
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_col_len);
5733 8 goto err_exit_no_heap;
5734 }
5735 }
5736
5737 /* Check existing index definitions for too-long column
5738 prefixes as well, in case max_col_len shrunk. */
5739
3/4
✓ Branch 0 taken 47707 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 64635 times.
✓ Branch 3 taken 47707 times.
224684 for (const dict_index_t *index = indexed_table->first_index(); index;
5740
1/2
✓ Branch 0 taken 64635 times.
✗ Branch 1 not taken.
129270 index = index->next()) {
5741
2/2
✓ Branch 0 taken 1124 times.
✓ Branch 1 taken 63511 times.
129270 if (index->type & DICT_FTS) {
5742
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1124 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2248 assert(index->type == DICT_FTS || index->is_corrupted());
5743
5744 /* We need to drop any corrupted fts indexes
5745 before we add a new fts index. */
5746
3/4
✓ Branch 0 taken 941 times.
✓ Branch 1 taken 183 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 941 times.
2248 if (add_fts_idx && index->type & DICT_CORRUPT) {
5747 ib_errf(m_user_thd, IB_LOG_LEVEL_ERROR, ER_INNODB_INDEX_CORRUPT,
5748 "Fulltext index '%s' is corrupt. "
5749 "you should drop this index first.",
5750 index->name());
5751
5752 goto err_exit_no_heap;
5753 }
5754
5755 2248 continue;
5756 }
5757
5758
3/4
✓ Branch 0 taken 537430 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 473919 times.
✓ Branch 3 taken 63511 times.
1074860 for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
5759
1/2
✓ Branch 0 taken 473919 times.
✗ Branch 1 not taken.
947838 const dict_field_t *field = index->get_field(i);
5760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 473919 times.
947838 if (field->prefix_len > max_col_len) {
5761 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_col_len);
5762 goto err_exit_no_heap;
5763 }
5764 }
5765 }
5766
5767 95414 n_drop_index = 0;
5768 95414 n_drop_fk = 0;
5769
5770
1/2
✓ Branch 0 taken 47707 times.
✗ Branch 1 not taken.
95414 if (ha_alter_info->handler_flags &
5771 (INNOBASE_ALTER_NOREBUILD | INNOBASE_ALTER_REBUILD)) {
5772
1/2
✓ Branch 0 taken 47707 times.
✗ Branch 1 not taken.
95414 heap = mem_heap_create(1024, UT_LOCATION_HERE);
5773
5774
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 47643 times.
95414 if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME) {
5775
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
128 col_names = innobase_get_col_names(ha_alter_info, altered_table, table,
5776 indexed_table, heap);
5777 } else {
5778 95286 col_names = nullptr;
5779 }
5780 } else {
5781 heap = nullptr;
5782 col_names = nullptr;
5783 }
5784
5785
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 47669 times.
95414 if (ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) {
5786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
76 assert(ha_alter_info->alter_info->drop_list.size() > 0);
5787
5788 drop_fk = static_cast<dict_foreign_t **>(
5789
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
76 mem_heap_alloc(heap, ha_alter_info->alter_info->drop_list.size() *
5790 sizeof(dict_foreign_t *)));
5791
5792
3/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 38 times.
164 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
5793
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 39 times.
88 if (drop->type != Alter_drop::FOREIGN_KEY) {
5794 10 continue;
5795 }
5796
5797 78 for (dict_foreign_set::iterator it =
5798 78 m_prebuilt->table->foreign_set.begin();
5799
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
86 it != m_prebuilt->table->foreign_set.end(); ++it) {
5800 86 dict_foreign_t *foreign = *it;
5801 86 const char *fid = strchr(foreign->id, '/');
5802
5803
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
86 assert(fid);
5804 /* If no database/ prefix was present in
5805 the FOREIGN KEY constraint name, compare
5806 to the full constraint name. */
5807
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
86 fid = fid ? fid + 1 : foreign->id;
5808
5809
3/4
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 4 times.
86 if (!my_strcasecmp(system_charset_info, fid, drop->name)) {
5810 78 drop_fk[n_drop_fk++] = foreign;
5811 78 goto found_fk;
5812 }
5813 }
5814
5815 /*
5816 Since we check that foreign key to be dropped exists on SQL-layer,
5817 we should not come here unless there is some bug and data-dictionary
5818 and InnoDB dictionary cache got out of sync.
5819 */
5820 assert(0);
5821 my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->name);
5822 goto err_exit;
5823 78 found_fk:
5824 78 continue;
5825 }
5826
5827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
76 assert(n_drop_fk > 0);
5828 } else {
5829 95338 drop_fk = nullptr;
5830 }
5831
5832
2/2
✓ Branch 0 taken 1932 times.
✓ Branch 1 taken 45775 times.
95414 if (ha_alter_info->index_drop_count) {
5833 3864 dict_index_t *drop_primary = nullptr;
5834
5835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1932 times.
3864 assert(ha_alter_info->handler_flags &
5836 (Alter_inplace_info::DROP_INDEX |
5837 Alter_inplace_info::DROP_UNIQUE_INDEX |
5838 Alter_inplace_info::DROP_PK_INDEX));
5839 /* Check which indexes to drop. */
5840 7728 drop_index = static_cast<dict_index_t **>(mem_heap_alloc(
5841
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
3864 heap, (ha_alter_info->index_drop_count + 1) * sizeof *drop_index));
5842
5843
2/2
✓ Branch 0 taken 1987 times.
✓ Branch 1 taken 1932 times.
7838 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
5844 3974 const KEY *key = ha_alter_info->index_drop_buffer[i];
5845 dict_index_t *index =
5846
1/2
✓ Branch 0 taken 1987 times.
✗ Branch 1 not taken.
3974 dict_table_get_index_on_name(indexed_table, key->name);
5847
5848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1987 times.
3974 if (!index) {
5849 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
5850 HA_ERR_WRONG_INDEX,
5851 "InnoDB could not find key"
5852 " with name %s",
5853 key->name);
5854 } else {
5855
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1987 times.
3974 ut_ad(!index->to_be_dropped);
5856
3/4
✓ Branch 0 taken 1987 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1313 times.
✓ Branch 3 taken 674 times.
3974 if (!index->is_clustered()) {
5857 2626 drop_index[n_drop_index++] = index;
5858 } else {
5859 1348 drop_primary = index;
5860 }
5861 }
5862 }
5863
5864 /* If all FULLTEXT indexes were removed, drop an
5865 internal FTS_DOC_ID_INDEX as well, unless it exists in
5866 the table. */
5867
5868 3864 if (innobase_fulltext_exist(table) &&
5869
6/6
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 1801 times.
✓ Branch 2 taken 112 times.
✓ Branch 3 taken 19 times.
✓ Branch 4 taken 1915 times.
✓ Branch 5 taken 17 times.
4088 !innobase_fulltext_exist(altered_table) &&
5870
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 95 times.
224 !DICT_TF2_FLAG_IS_SET(indexed_table, DICT_TF2_FTS_HAS_DOC_ID)) {
5871 34 dict_index_t *fts_doc_index = indexed_table->fts_doc_id_index;
5872
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 ut_ad(fts_doc_index);
5873
5874 // Add some fault tolerance for non-debug builds.
5875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if (fts_doc_index == nullptr) {
5876 goto check_if_can_drop_indexes;
5877 }
5878
5879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 assert(!fts_doc_index->to_be_dropped);
5880
5881
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 5 times.
74 for (uint i = 0; i < table->s->keys; i++) {
5882
3/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 20 times.
64 if (!my_strcasecmp(system_charset_info, FTS_DOC_ID_INDEX_NAME,
5883 table->key_info[i].name)) {
5884 /* The index exists in the MySQL
5885 data dictionary. Do not drop it,
5886 even though it is no longer needed
5887 by InnoDB fulltext search. */
5888 24 goto check_if_can_drop_indexes;
5889 }
5890 }
5891
5892 10 drop_index[n_drop_index++] = fts_doc_index;
5893 }
5894
5895 3830 check_if_can_drop_indexes:
5896 /* Check if the indexes can be dropped. */
5897
5898 /* Prevent a race condition between DROP INDEX and
5899 CREATE TABLE adding FOREIGN KEY constraints. */
5900
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
3864 row_mysql_lock_data_dictionary(m_prebuilt->trx, UT_LOCATION_HERE);
5901
5902
2/2
✓ Branch 0 taken 666 times.
✓ Branch 1 taken 1266 times.
3864 if (!n_drop_index) {
5903 1332 drop_index = nullptr;
5904 } else {
5905 /* Flag all indexes that are to be dropped. */
5906
2/2
✓ Branch 0 taken 1318 times.
✓ Branch 1 taken 1266 times.
5168 for (ulint i = 0; i < n_drop_index; i++) {
5907
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1318 times.
2636 ut_ad(!drop_index[i]->to_be_dropped);
5908 2636 drop_index[i]->to_be_dropped = 1;
5909 }
5910 }
5911
5912
2/2
✓ Branch 0 taken 1318 times.
✓ Branch 1 taken 1932 times.
6500 for (uint i = 0; i < n_drop_index; i++) {
5913 2636 dict_index_t *index = drop_index[i];
5914
5915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1318 times.
2636 if (innobase_check_foreign_key_index(ha_alter_info, index, indexed_table,
5916
1/2
✓ Branch 0 taken 1318 times.
✗ Branch 1 not taken.
2636 col_names, m_prebuilt->trx, drop_fk,
5917 n_drop_fk)) {
5918 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5919 m_prebuilt->trx->error_index = index;
5920 print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
5921 goto err_exit;
5922 }
5923 }
5924
5925 /* If a primary index is dropped, need to check
5926 any depending foreign constraints get affected */
5927
4/6
✓ Branch 0 taken 674 times.
✓ Branch 1 taken 1258 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 674 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1932 times.
5212 if (drop_primary && innobase_check_foreign_key_index(
5928 ha_alter_info, drop_primary, indexed_table,
5929
1/2
✓ Branch 0 taken 674 times.
✗ Branch 1 not taken.
1348 col_names, m_prebuilt->trx, drop_fk, n_drop_fk)) {
5930 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5931 print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
5932 goto err_exit;
5933 }
5934
5935
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
3864 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5936 } else {
5937 91550 drop_index = nullptr;
5938 }
5939
5940 95414 n_rename_index = ha_alter_info->index_rename_count;
5941 95414 rename_index = nullptr;
5942
5943 /* Create a list of dict_index_t objects that are to be renamed,
5944 also checking for requests to rename nonexistent indexes. If
5945 the table is going to be rebuilt (new_clustered == true in
5946 prepare_inplace_alter_table_dict()), then this can be skipped,
5947 but we don't for simplicity (we have not determined the value of
5948 new_clustered yet). */
5949
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 47623 times.
95414 if (n_rename_index > 0) {
5950 rename_index = static_cast<dict_index_t **>(
5951
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
168 mem_heap_alloc(heap, n_rename_index * sizeof(*rename_index)));
5952
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 84 times.
338 for (ulint i = 0; i < n_rename_index; i++) {
5953 dict_index_t *index;
5954 170 const char *old_name =
5955 170 ha_alter_info->index_rename_buffer[i].old_key->name;
5956
5957
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
170 index = dict_table_get_index_on_name(indexed_table, old_name);
5958
5959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
170 if (index == nullptr) {
5960 my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), old_name,
5961 m_prebuilt->table->name.m_name);
5962 goto err_exit;
5963 }
5964
5965 170 rename_index[i] = index;
5966 }
5967 }
5968
5969 95414 n_add_fk = 0;
5970
5971
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 47613 times.
95414 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_FOREIGN_KEY) {
5972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
188 ut_ad(!m_prebuilt->trx->check_foreigns);
5973
5974
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
188 alter_fill_stored_column(altered_table, m_prebuilt->table, &s_cols,
5975 &s_heap);
5976
5977
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
188 add_fk = static_cast<dict_foreign_t **>(mem_heap_zalloc(
5978 heap,
5979 188 ha_alter_info->alter_info->key_list.size() * sizeof(dict_foreign_t *)));
5980
5981
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 90 times.
188 if (!innobase_get_foreign_key_info(ha_alter_info, table_share,
5982 188 m_prebuilt->table, col_names, drop_index,
5983 n_drop_index, add_fk, &n_add_fk,
5984
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
188 m_prebuilt->trx, s_cols)) {
5985 8 err_exit:
5986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 if (n_drop_index) {
5987 row_mysql_lock_data_dictionary(m_prebuilt->trx, UT_LOCATION_HERE);
5988
5989 /* Clear the to_be_dropped flags, which might
5990 have been set at this point. */
5991 for (ulint i = 0; i < n_drop_index; i++) {
5992 ut_ad(drop_index[i]->is_committed());
5993 drop_index[i]->to_be_dropped = 0;
5994 }
5995
5996 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5997 }
5998
5999
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 if (heap) {
6000
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 mem_heap_free(heap);
6001 }
6002
6003
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
10 if (s_cols != nullptr) {
6004 2 ut::delete_(s_cols);
6005
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 mem_heap_free(s_heap);
6006 }
6007
6008 10 goto err_exit_no_heap;
6009 }
6010
6011
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 87 times.
180 if (s_cols != nullptr) {
6012 6 ut::delete_(s_cols);
6013
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 mem_heap_free(s_heap);
6014 }
6015 }
6016
6017
4/4
✓ Branch 0 taken 46272 times.
✓ Branch 1 taken 1431 times.
✓ Branch 2 taken 11940 times.
✓ Branch 3 taken 35763 times.
187950 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA) ||
6018
2/2
✓ Branch 0 taken 22108 times.
✓ Branch 1 taken 24164 times.
92544 ((ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ==
6019 44216 Alter_inplace_info::CHANGE_CREATE_OPTION &&
6020
2/2
✓ Branch 0 taken 10509 times.
✓ Branch 1 taken 11599 times.
44216 !innobase_need_rebuild(
6021 44216 ha_alter_info, table,
6022
1/2
✓ Branch 0 taken 22108 times.
✗ Branch 1 not taken.
44216 dict_table_is_file_per_table(m_prebuilt->table)))) {
6023
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 if (heap) {
6024
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
47760 ha_alter_info->handler_ctx = new (m_user_thd->mem_root)
6025 ha_innobase_inplace_ctx(m_prebuilt, drop_index, n_drop_index,
6026 rename_index, n_rename_index, drop_fk,
6027 n_drop_fk, add_fk, n_add_fk,
6028
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 ha_alter_info->online, heap, indexed_table,
6029 col_names, ULINT_UNDEFINED, 0, 0);
6030 }
6031
6032
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11940 times.
23880 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
6033
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
6034
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 online_retry_drop_dict_indexes(m_prebuilt->table, false);
6035 }
6036
6037 47760 if ((ha_alter_info->handler_flags &
6038
3/4
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 11853 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11940 times.
24054 Alter_inplace_info::DROP_VIRTUAL_COLUMN) &&
6039
2/4
✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 87 times.
174 prepare_inplace_drop_virtual(ha_alter_info, table)) {
6040 return true;
6041 }
6042
6043 47760 if ((ha_alter_info->handler_flags &
6044
3/4
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 11793 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11940 times.
24174 Alter_inplace_info::ADD_VIRTUAL_COLUMN) &&
6045
2/4
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 147 times.
294 prepare_inplace_add_virtual(ha_alter_info, altered_table, table)) {
6046 return true;
6047 }
6048
6049
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 if (ha_alter_info->handler_ctx != nullptr) {
6050 23880 ha_innobase_inplace_ctx *ctx =
6051 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6052 47760 if ((ha_alter_info->handler_flags &
6053 21018 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
6054 21018 ha_alter_info->create_info
6055
5/6
✓ Branch 0 taken 10509 times.
✓ Branch 1 taken 1431 times.
✓ Branch 2 taken 5032 times.
✓ Branch 3 taken 5477 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 11940 times.
33944 ->m_implicit_tablespace_autoextend_size_change &&
6056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5032 times.
10064 prepare_inplace_change_implicit_tablespace_option(
6057
1/2
✓ Branch 0 taken 5032 times.
✗ Branch 1 not taken.
10064 m_user_thd, ha_alter_info, ctx->old_table)) {
6058 return true;
6059 }
6060
1/2
✓ Branch 0 taken 11940 times.
✗ Branch 1 not taken.
23880 return dd_prepare_inplace_alter_table(m_user_thd, ctx->old_table,
6061 23880 ctx->new_table, old_dd_tab);
6062 } else {
6063 return false;
6064 }
6065 }
6066
6067 /* If we are to build a full-text search index, check whether
6068 the table already has a DOC ID column. If not, we will need to
6069 add a Doc ID hidden column and rebuild the primary index */
6070
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 35273 times.
71526 if (innobase_fulltext_exist(altered_table)) {
6071 ulint doc_col_no;
6072 980 ulint num_v = 0;
6073
6074
3/4
✓ Branch 0 taken 490 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 368 times.
980 if (!innobase_fts_check_doc_id_col(m_prebuilt->table, altered_table,
6075 &fts_doc_col_no, &num_v)) {
6076 244 fts_doc_col_no = altered_table->s->fields - num_v;
6077 244 add_fts_doc_id = true;
6078 244 add_fts_doc_id_idx = true;
6079
6080
1/2
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
244 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
6081 HA_ERR_WRONG_INDEX,
6082 "InnoDB rebuilding table to add"
6083 " column " FTS_DOC_ID_COL_NAME);
6084
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 367 times.
736 } else if (fts_doc_col_no == ULINT_UNDEFINED) {
6085 2 goto err_exit;
6086 }
6087
6088
3/6
✓ Branch 0 taken 489 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 137 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 352 times.
✗ Branch 5 not taken.
978 switch (innobase_fts_check_doc_id_index(m_prebuilt->table, altered_table,
6089 &doc_col_no)) {
6090 274 case FTS_NOT_EXIST_DOC_ID_INDEX:
6091 274 add_fts_doc_id_idx = true;
6092 274 break;
6093 case FTS_INCORRECT_DOC_ID_INDEX:
6094 my_error(ER_INNODB_FT_WRONG_DOCID_INDEX, MYF(0), FTS_DOC_ID_INDEX_NAME);
6095 goto err_exit;
6096 704 case FTS_EXIST_DOC_ID_INDEX:
6097
3/6
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 336 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
704 assert(doc_col_no == fts_doc_col_no || doc_col_no == ULINT_UNDEFINED ||
6098 (ha_alter_info->handler_flags &
6099 (Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
6100 Alter_inplace_info::DROP_STORED_COLUMN |
6101 Alter_inplace_info::ADD_STORED_BASE_COLUMN)));
6102 }
6103 }
6104
6105 /* See if an AUTO_INCREMENT column was added. */
6106 71524 uint i = 0;
6107 71524 ulint num_v = 0;
6108 71524 List_iterator_fast<Create_field> cf_it(
6109
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 ha_alter_info->alter_info->create_list);
6110
2/2
✓ Branch 0 taken 271249 times.
✓ Branch 1 taken 35762 times.
614022 while (const Create_field *new_field = cf_it++) {
6111 const Field *field;
6112
6113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 271249 times.
542498 assert(i < altered_table->s->fields);
6114
6115
2/2
✓ Branch 0 taken 3558620 times.
✓ Branch 1 taken 17936 times.
7153112 for (uint old_i = 0; table->field[old_i]; old_i++) {
6116
2/2
✓ Branch 0 taken 253313 times.
✓ Branch 1 taken 3305307 times.
7117240 if (new_field->field == table->field[old_i]) {
6117 506626 goto found_col;
6118 }
6119 }
6120
6121 /* This is an added column. */
6122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17936 times.
35872 assert(!new_field->field);
6123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17936 times.
35872 assert(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
6124
6125 35872 field = altered_table->field[i];
6126
6127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17936 times.
35872 assert(((field->auto_flags & Field::NEXT_NUMBER) != 0) ==
6128 field->is_flag_set(AUTO_INCREMENT_FLAG));
6129
6130
2/2
✓ Branch 0 taken 17895 times.
✓ Branch 1 taken 41 times.
35872 if (field->is_flag_set(AUTO_INCREMENT_FLAG)) {
6131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
82 if (add_autoinc_col_no != ULINT_UNDEFINED) {
6132 /* This should have been blocked earlier. */
6133 my_error(ER_WRONG_AUTO_KEY, MYF(0));
6134 ut_d(ut_error);
6135 ut_o(goto err_exit);
6136 }
6137
6138 /* Get the col no of the old table non-virtual column array */
6139 82 add_autoinc_col_no = i - num_v;
6140
6141
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
82 autoinc_col_max_value = field->get_max_int_value();
6142 }
6143 35790 found_col:
6144
4/4
✓ Branch 0 taken 1514 times.
✓ Branch 1 taken 269735 times.
✓ Branch 2 taken 1398 times.
✓ Branch 3 taken 116 times.
542498 if (innobase_is_v_fld(new_field)) {
6145 2796 ++num_v;
6146 }
6147
6148 542498 i++;
6149 }
6150
6151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(heap);
6152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(m_user_thd == m_prebuilt->trx->mysql_thd);
6153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35762 times.
71524 assert(!ha_alter_info->handler_ctx);
6154
6155
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
143048 ha_alter_info->handler_ctx = new (m_user_thd->mem_root)
6156 ha_innobase_inplace_ctx(m_prebuilt, drop_index, n_drop_index,
6157 rename_index, n_rename_index, drop_fk, n_drop_fk,
6158 71524 add_fk, n_add_fk, ha_alter_info->online, heap,
6159 71524 m_prebuilt->table, col_names, add_autoinc_col_no,
6160
1/2
✓ Branch 0 taken 35762 times.
✗ Branch 1 not taken.
71524 ha_alter_info->create_info->auto_increment_value,
6161 autoinc_col_max_value);
6162
6163 71524 return prepare_inplace_alter_table_dict(
6164 71524 ha_alter_info, altered_table, table, old_dd_tab, new_dd_tab,
6165
1/2
✓ Branch 0 taken 35623 times.
✗ Branch 1 not taken.
143048 table_share->table_name.str, info.flags(), info.flags2() | encrypt_flag,
6166 71246 fts_doc_col_no, add_fts_doc_id, add_fts_doc_id_idx, m_prebuilt);
6167 112728 }
6168
6169 /** Check that the column is part of a virtual index(index contains
6170 virtual column) in the table
6171 @param[in] table Table containing column
6172 @param[in] col column to be checked
6173 @return true if this column is indexed with other virtual columns */
6174 93 static bool dict_col_in_v_indexes(dict_table_t *table, dict_col_t *col) {
6175
2/2
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 93 times.
279 for (dict_index_t *index = table->first_index()->next(); index != nullptr;
6176 186 index = index->next()) {
6177
1/2
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
186 if (!dict_index_has_virtual(index)) {
6178 186 continue;
6179 }
6180 for (ulint k = 0; k < index->n_fields; k++) {
6181 dict_field_t *field = index->get_field(k);
6182 if (field->col->ind == col->ind) {
6183 return (true);
6184 }
6185 }
6186 }
6187
6188 93 return (false);
6189 }
6190
6191 /* Check whether a columnn length change alter operation requires
6192 to rebuild the template.
6193 @param[in] altered_table TABLE object for new version of table.
6194 @param[in] ha_alter_info Structure describing changes to be done
6195 by ALTER TABLE and holding data used
6196 during in-place alter.
6197 @param[in] table table being altered
6198 @return true if needs rebuild. */
6199 6 static bool alter_templ_needs_rebuild(TABLE *altered_table,
6200 Alter_inplace_info *ha_alter_info,
6201 dict_table_t *table) {
6202 6 ulint i = 0;
6203 List_iterator_fast<Create_field> cf_it(
6204
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 ha_alter_info->alter_info->create_list);
6205
6206
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
16 for (Field **fp = altered_table->field; *fp; fp++, i++) {
6207 10 cf_it.rewind();
6208
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 10 times.
30 while (const Create_field *cf = cf_it++) {
6209
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 20 times.
126 for (ulint j = 0; j < table->n_cols; j++) {
6210
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 dict_col_t *cols = table->get_col(j);
6211
4/6
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 106 times.
199 if (cf->max_display_width_in_bytes() > cols->len &&
6212
2/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 93 times.
93 dict_col_in_v_indexes(table, cols)) {
6213 return (true);
6214 }
6215 }
6216 20 }
6217 }
6218
6219 6 return (false);
6220 }
6221
6222 /** Get the name of an erroneous key.
6223 @param[in] error_key_num InnoDB number of the erroneus key
6224 @param[in] ha_alter_info changes that were being performed
6225 @param[in] table InnoDB table
6226 @return the name of the erroneous key */
6227 2 static const char *get_error_key_name(ulint error_key_num,
6228 const Alter_inplace_info *ha_alter_info,
6229 const dict_table_t *table) {
6230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (error_key_num == ULINT_UNDEFINED) {
6231 return (FTS_DOC_ID_INDEX_NAME);
6232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (ha_alter_info->key_count == 0) {
6233 return (table->first_index()->name);
6234 } else {
6235 2 return (ha_alter_info->key_info_buffer[error_key_num].name);
6236 }
6237 }
6238
6239 template <typename Table>
6240 112052 bool ha_innobase::inplace_alter_table_impl(TABLE *altered_table,
6241 Alter_inplace_info *ha_alter_info) {
6242 112052 dict_add_v_col_t *add_v = nullptr;
6243 112052 dict_vcol_templ_t *s_templ = nullptr;
6244 112052 dict_vcol_templ_t *old_templ = nullptr;
6245 112052 struct TABLE *eval_table = altered_table;
6246 112052 bool rebuild_templ = false;
6247
1/2
✓ Branch 0 taken 56026 times.
✗ Branch 1 not taken.
112052 DBUG_TRACE;
6248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56026 times.
112052 assert(!srv_read_only_mode);
6249
6250
2/4
✓ Branch 0 taken 56026 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56026 times.
112052 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_X));
6251
2/4
✓ Branch 0 taken 56026 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56026 times.
112052 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_S));
6252
6253
3/4
✓ Branch 0 taken 54364 times.
✓ Branch 1 taken 1662 times.
✓ Branch 2 taken 54364 times.
✗ Branch 3 not taken.
112052 DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter");
6254
6255 222224 auto all_ok = [=]() -> bool {
6256
3/4
✓ Branch 0 taken 4125 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 50130 times.
✓ Branch 3 taken 1662 times.
55917 DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table");
6257 55917 return false;
6258 };
6259
6260 218177 auto success = [&]() -> bool {
6261 35375 ut_d(dict_sys_mutex_enter());
6262 35375 ut_d(dict_table_check_for_dup_indexes(m_prebuilt->table, CHECK_PARTIAL_OK));
6263 35375 ut_d(dict_sys_mutex_exit());
6264 /* prebuilt->table->n_ref_count can be anything here,
6265 given that we hold at most a shared lock on the table. */
6266 35375 return all_ok();
6267 };
6268
6269
6/6
✓ Branch 0 taken 47721 times.
✓ Branch 1 taken 8305 times.
✓ Branch 2 taken 1728 times.
✓ Branch 3 taken 45993 times.
✓ Branch 4 taken 10033 times.
✓ Branch 5 taken 45993 times.
207494 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA) ||
6270 95442 is_instant(ha_alter_info)) {
6271
1/2
✓ Branch 0 taken 10033 times.
✗ Branch 1 not taken.
20066 return all_ok();
6272 }
6273
6274 183972 if (((ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ==
6275
4/4
✓ Branch 0 taken 22103 times.
✓ Branch 1 taken 23890 times.
✓ Branch 2 taken 10509 times.
✓ Branch 3 taken 35484 times.
136192 Alter_inplace_info::CHANGE_CREATE_OPTION &&
6276
2/2
✓ Branch 0 taken 10509 times.
✓ Branch 1 taken 11594 times.
44206 !innobase_need_rebuild(
6277 44206 ha_alter_info, table,
6278
1/2
✓ Branch 0 taken 22103 times.
✗ Branch 1 not taken.
44206 dict_table_is_file_per_table(m_prebuilt->table)))) {
6279
1/2
✓ Branch 0 taken 10509 times.
✗ Branch 1 not taken.
21018 return all_ok();
6280 }
6281
6282 70968 ha_innobase_inplace_ctx *ctx =
6283 70968 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6284
6285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35484 times.
70968 assert(ctx);
6286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35484 times.
70968 assert(ctx->trx);
6287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35484 times.
70968 assert(ctx->prebuilt == m_prebuilt);
6288
6289
1/2
✓ Branch 0 taken 35484 times.
✗ Branch 1 not taken.
70968 dict_index_t *pk = m_prebuilt->table->first_index();
6290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35484 times.
70968 ut_ad(pk != nullptr);
6291
6292 /* For partitioned tables this could be already allocated from a
6293 previous partition invocation. For normal tables this is NULL. */
6294 70968 ut::delete_(ctx->m_stage);
6295
6296
1/2
✓ Branch 0 taken 35484 times.
✗ Branch 1 not taken.
70968 ctx->m_stage = ut::new_withkey<Alter_stage>(UT_NEW_THIS_FILE_PSI_KEY, pk);
6297
6298
5/6
✓ Branch 0 taken 35476 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35476 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 35476 times.
141920 if (m_prebuilt->table->ibd_file_missing ||
6299 70952 dict_table_is_discarded(m_prebuilt->table)) {
6300
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
16 return success();
6301 }
6302
6303 /* If we are doing a table rebuilding or having added virtual
6304 columns in the same clause, we will need to build a table template
6305 that carries translation information between MySQL TABLE and InnoDB
6306 table, which indicates the virtual columns and their base columns
6307 info. This is used to do the computation callback, so that the
6308 data in base columns can be extracted send to server.
6309 If the Column length changes and it is a part of virtual
6310 index then we need to rebuild the template. */
6311 70952 rebuild_templ =
6312
2/2
✓ Branch 0 taken 5175 times.
✓ Branch 1 taken 30301 times.
81302 ctx->need_rebuild() ||
6313
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5169 times.
10350 ((ha_alter_info->handler_flags &
6314 12 Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) &&
6315
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
12 alter_templ_needs_rebuild(altered_table, ha_alter_info, ctx->new_table));
6316
6317
4/4
✓ Branch 0 taken 1061 times.
✓ Branch 1 taken 34415 times.
✓ Branch 2 taken 823 times.
✓ Branch 3 taken 238 times.
70952 if ((ctx->new_table->n_v_cols > 0) && rebuild_templ) {
6318 /* Save the templ if isn't NULL so as to restore the
6319 original state in case of alter operation failures. */
6320
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 823 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 823 times.
1646 if (ctx->new_table->vc_templ != nullptr && !ctx->need_rebuild()) {
6321 old_templ = ctx->new_table->vc_templ;
6322 }
6323
1/2
✓ Branch 0 taken 823 times.
✗ Branch 1 not taken.
1646 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
6324 1646 s_templ->vtempl = nullptr;
6325
6326
1/2
✓ Branch 0 taken 823 times.
✗ Branch 1 not taken.
1646 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, nullptr,
6327 false, nullptr);
6328
6329 1646 ctx->new_table->vc_templ = s_templ;
6330
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 34484 times.
69306 } else if (ha_alter_info->virtual_column_add_count > 0 &&
6331
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 1 times.
338 ha_alter_info->virtual_column_drop_count == 0) {
6332 /* if there is ongoing drop virtual column, then we disallow
6333 inplace add index on newly added virtual column, so it does
6334 not need to come in here to rebuild template with add_v.
6335 Please also see the assertion in innodb_v_adjust_idx_col() */
6336
6337
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
336 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
6338
6339 add_v = static_cast<dict_add_v_col_t *>(
6340
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
336 mem_heap_alloc(ctx->heap, sizeof *add_v));
6341 336 add_v->n_v_col = ha_alter_info->virtual_column_add_count;
6342 336 add_v->v_col = ctx->add_vcol;
6343 336 add_v->v_col_name = ctx->add_vcol_name;
6344
6345 336 s_templ->vtempl = nullptr;
6346
6347
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
336 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, add_v, false,
6348 nullptr);
6349 336 old_templ = ctx->new_table->vc_templ;
6350 336 ctx->new_table->vc_templ = s_templ;
6351 }
6352
6353 /* Drop virtual column without rebuild will keep dict table
6354 unchanged, we use old table to evaluate virtual column value
6355 in innobase_get_computed_value(). */
6356
6/6
✓ Branch 0 taken 5175 times.
✓ Branch 1 taken 30301 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 5169 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 35470 times.
70952 if (!ctx->need_rebuild() && ha_alter_info->virtual_column_drop_count > 0) {
6357 12 eval_table = table;
6358 }
6359
6360 281701 auto clean_up = [&](dberr_t err) -> bool {
6361
3/4
✓ Branch 0 taken 2958 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31679 times.
✓ Branch 3 taken 839 times.
35476 DEBUG_SYNC_C("alter_table_update_log");
6362
6363
16/16
✓ Branch 0 taken 2956 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2947 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 2571 times.
✓ Branch 5 taken 376 times.
✓ Branch 6 taken 2571 times.
✓ Branch 7 taken 387 times.
✓ Branch 8 taken 32421 times.
✓ Branch 9 taken 97 times.
✓ Branch 10 taken 31590 times.
✓ Branch 11 taken 831 times.
✓ Branch 12 taken 27409 times.
✓ Branch 13 taken 4181 times.
✓ Branch 14 taken 27409 times.
✓ Branch 15 taken 5109 times.
35476 if (err == DB_SUCCESS && ctx->online && ctx->need_rebuild()) {
6364
3/4
✓ Branch 0 taken 2571 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26570 times.
✓ Branch 3 taken 839 times.
29980 DEBUG_SYNC_C("row_log_table_apply1_before");
6365 29980 err = row_log_table_apply(ctx->thr, m_prebuilt->table, altered_table,
6366 ctx->m_stage);
6367 }
6368
6369
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2958 times.
✓ Branch 2 taken 991 times.
✓ Branch 3 taken 31527 times.
35476 if (s_templ) {
6370
4/16
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 168 times.
✓ Branch 9 taken 823 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 168 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 991 times.
991 ut_ad(ctx->need_rebuild() ||
6371 ha_alter_info->virtual_column_add_count > 0 || rebuild_templ);
6372 991 dict_free_vc_templ(s_templ);
6373 991 ut::delete_(s_templ);
6374
6375 991 ctx->new_table->vc_templ = old_templ;
6376 }
6377
6378
3/4
✓ Branch 0 taken 2958 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31679 times.
✓ Branch 3 taken 839 times.
35476 DEBUG_SYNC_C("inplace_after_index_build");
6379
6380
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2958 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 32517 times.
35476 DBUG_EXECUTE_IF("create_index_fail", err = DB_DUPLICATE_KEY;
6381 m_prebuilt->trx->error_key_num = ULINT_UNDEFINED;);
6382
6383 /* After an error, remove all those index definitions
6384 from the dictionary which were defined. */
6385
6386
6/10
✓ Branch 0 taken 2956 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32411 times.
✓ Branch 6 taken 37 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 68 times.
35476 switch (err) {
6387 35367 case DB_SUCCESS:
6388 35367 return success();
6389 39 case DB_DUPLICATE_KEY: {
6390 39 KEY *dup_key{};
6391
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 1 times.
39 if (m_prebuilt->trx->error_key_num == ULINT_UNDEFINED ||
6392
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
38 ha_alter_info->key_count == 0) {
6393 /* This should be the hidden index on
6394 FTS_DOC_ID, or there is no PRIMARY KEY in the
6395 table. Either way, we should be seeing and
6396 reporting a bogus duplicate key error. */
6397
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 14 times.
38 } else if (m_prebuilt->trx->error_key_num == 0) {
6398 23 dup_key =
6399 23 &ha_alter_info->key_info_buffer[m_prebuilt->trx->error_key_num];
6400 } else {
6401 /* Check if there is generated cluster index column */
6402
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
15 if (ctx->num_to_add_index > ha_alter_info->key_count) {
6403
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 assert(m_prebuilt->trx->error_key_num <= ha_alter_info->key_count);
6404 2 dup_key =
6405 &ha_alter_info
6406 2 ->key_info_buffer[m_prebuilt->trx->error_key_num - 1];
6407 } else {
6408
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
13 assert(m_prebuilt->trx->error_key_num < ha_alter_info->key_count);
6409 13 dup_key =
6410 13 &ha_alter_info->key_info_buffer[m_prebuilt->trx->error_key_num];
6411 }
6412 }
6413 39 print_keydup_error(altered_table, dup_key, MYF(0),
6414 39 table_share->table_name.str);
6415 39 break;
6416 }
6417 2 case DB_ONLINE_LOG_TOO_BIG:
6418
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 assert(ctx->online);
6419 2 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
6420 2 get_error_key_name(m_prebuilt->trx->error_key_num,
6421 2 ha_alter_info, m_prebuilt->table));
6422 2 break;
6423 case DB_INDEX_CORRUPT:
6424 my_error(ER_INDEX_CORRUPT, MYF(0),
6425 get_error_key_name(m_prebuilt->trx->error_key_num,
6426 ha_alter_info, m_prebuilt->table));
6427 break;
6428 68 default:
6429 68 my_error_innodb(err, table_share->table_name.str,
6430 68 m_prebuilt->table->flags);
6431 }
6432
6433 /* prebuilt->table->n_ref_count can be anything here, given
6434 that we hold at most a shared lock on the table. */
6435 109 m_prebuilt->trx->error_index = nullptr;
6436 109 ctx->trx->error_state = DB_SUCCESS;
6437
6438 109 return true;
6439 };
6440
6441 /* Read the clustered index of the table and build
6442 indexes based on this information using temporary
6443 files and merge sort. */
6444
4/6
✓ Branch 0 taken 35476 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 35474 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
70952 DBUG_EXECUTE_IF("innodb_OOM_inplace_alter",
6445 return clean_up(DB_OUT_OF_MEMORY););
6446
6447 70948 const auto trx = m_prebuilt->trx;
6448 70948 const auto old_isolation_level = trx->isolation_level;
6449
6450
2/2
✓ Branch 0 taken 34602 times.
✓ Branch 1 taken 872 times.
70948 if (ctx->online &&
6451
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 34549 times.
69204 trx->isolation_level != trx_t::isolation_level_t::REPEATABLE_READ) {
6452 /* We must scan the index at an isolation level >= READ COMMITTED, because
6453 a dirty read will see half written blob references.
6454
6455 ** Perform a REPEATABLE READ.
6456 When rebuilding the table online, row_log_table_apply() must not see
6457 a newer state of the table when applying the log. This is mainly to
6458 prevent false duplicate key errors, because the log will identify records
6459 by the PRIMARY KEY, and also to prevent unsafe BLOB access.
6460
6461 When creating a secondary index online, this table scan must not see
6462 records that have only been inserted to the clustered index, but have
6463 not been written to the online_log of index[]. If we performed
6464 READ UNCOMMITTED, it could happen that the ADD INDEX reaches
6465 ONLINE_INDEX_COMPLETE state between the time the DML thread has updated
6466 the clustered index but has not yet accessed secondary index. */
6467
6468 106 trx->isolation_level = trx_t::isolation_level_t::REPEATABLE_READ;
6469 }
6470
6471 212844 ddl::Context ddl(trx, m_prebuilt->table, ctx->new_table, ctx->online,
6472 ctx->add_index, ctx->add_key_numbers, ctx->num_to_add_index,
6473 70948 altered_table, ctx->add_cols, ctx->col_map, ctx->add_autoinc,
6474 70948 ctx->sequence, ctx->skip_pk_sort, ctx->m_stage, add_v,
6475
1/2
✓ Branch 0 taken 35474 times.
✗ Branch 1 not taken.
70948 eval_table, thd_ddl_buffer_size(m_prebuilt->trx->mysql_thd),
6476 70948 thd_ddl_threads(m_prebuilt->trx->mysql_thd));
6477
6478
1/2
✓ Branch 0 taken 35474 times.
✗ Branch 1 not taken.
70948 const auto err = clean_up(ddl.build());
6479
6480 70948 trx->isolation_level = old_isolation_level;
6481
6482 70948 return err;
6483 112052 }
6484
6485 /** Free the modification log for online table rebuild.
6486 @param table table that was being rebuilt online */
6487 30265 static void innobase_online_rebuild_log_free(dict_table_t *table) {
6488 30265 dict_index_t *clust_index = table->first_index();
6489
6490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30265 times.
30265 ut_ad(dict_sys_mutex_own());
6491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30265 times.
30265 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
6492
6493 30265 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
6494
6495
2/2
✓ Branch 0 taken 29977 times.
✓ Branch 1 taken 288 times.
30265 if (clust_index->online_log) {
6496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29977 times.
29977 ut_ad(dict_index_get_online_status(clust_index) == ONLINE_INDEX_CREATION);
6497 29977 clust_index->online_status = ONLINE_INDEX_COMPLETE;
6498 29977 row_log_free(clust_index->online_log);
6499
2/2
✓ Branch 0 taken 29138 times.
✓ Branch 1 taken 839 times.
29977 DEBUG_SYNC_C("innodb_online_rebuild_log_free_aborted");
6500 }
6501
6502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30265 times.
30265 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
6503 30265 rw_lock_x_unlock(&clust_index->lock);
6504 30265 }
6505
6506 /** Rollback a secondary index creation, drop the indexes with
6507 temparary index prefix
6508 @param user_table InnoDB table
6509 @param table the TABLE
6510 @param locked true=table locked, false=may need to do a lazy drop
6511 @param trx the transaction
6512 */
6513 82 static void innobase_rollback_sec_index(dict_table_t *user_table,
6514 const TABLE *table, bool locked,
6515 trx_t *trx) {
6516 82 ddl::drop_indexes(trx, user_table, locked);
6517
6518 /* Free the table->fts only if there is no FTS_DOC_ID
6519 in the table */
6520 168 if (user_table->fts &&
6521
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 82 times.
82 !DICT_TF2_FLAG_IS_SET(user_table, DICT_TF2_FTS_HAS_DOC_ID) &&
6522 !innobase_fulltext_exist(table)) {
6523 fts_free(user_table);
6524 }
6525 82 }
6526
6527 /** Roll back the changes made during prepare_inplace_alter_table()
6528 and inplace_alter_table() inside the storage engine. Note that the
6529 allowed level of concurrency during this operation will be the same as
6530 for inplace_alter_table() and thus might be higher than during
6531 prepare_inplace_alter_table(). (E.g concurrent writes were blocked
6532 during prepare, but might not be during commit).
6533
6534 @param[in] ha_alter_info Data used during in-place alter.
6535 @param[in] table the TABLE
6536 @param[in,out] prebuilt the prebuilt struct
6537 @retval true Failure
6538 @retval false Success
6539 */
6540 393 [[nodiscard]] inline bool rollback_inplace_alter_table(
6541 const Alter_inplace_info *ha_alter_info, const TABLE *table,
6542 row_prebuilt_t *prebuilt) {
6543 393 bool fail = false;
6544
6545 393 ha_innobase_inplace_ctx *ctx =
6546 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6547
6548
1/2
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
393 DBUG_TRACE;
6549
6550
4/4
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 132 times.
393 if (!ctx || !ctx->trx) {
6551 /* If we have not started a transaction yet,
6552 (almost) nothing has been or needs to be done. */
6553 261 goto func_exit;
6554 }
6555
6556
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 row_mysql_lock_data_dictionary(ctx->trx, UT_LOCATION_HERE);
6557
6558
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 75 times.
132 if (ctx->need_rebuild()) {
6559 /* The table could have been closed in commit phase */
6560
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 4 times.
57 if (ctx->new_table != nullptr) {
6561 53 dberr_t err = DB_SUCCESS;
6562 53 uint32_t flags = ctx->new_table->flags;
6563 /* DML threads can access ctx->new_table via the
6564 online rebuild log. Free it first. */
6565
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 innobase_online_rebuild_log_free(prebuilt->table);
6566
6567
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 dict_table_close(ctx->new_table, true, false);
6568
6569
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 switch (err) {
6570 53 case DB_SUCCESS:
6571 53 break;
6572 default:
6573 my_error_innodb(err, table->s->table_name.str, flags);
6574 fail = true;
6575 }
6576 }
6577 } else {
6578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 assert(!(ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX));
6579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 assert(ctx->new_table == prebuilt->table);
6580
6581
1/2
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
75 innobase_rollback_sec_index(prebuilt->table, table, false, ctx->trx);
6582 }
6583
6584
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 row_mysql_unlock_data_dictionary(ctx->trx);
6585
6586 393 func_exit:
6587 #ifdef UNIV_DEBUG
6588
1/2
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
393 dict_index_t *clust_index = prebuilt->table->first_index();
6589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 393 times.
393 assert(!clust_index->online_log);
6590
2/4
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 393 times.
393 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
6591 #endif /* UNIV_DEBUG */
6592
6593
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 259 times.
393 if (ctx) {
6594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
134 assert(ctx->prebuilt == prebuilt);
6595
6596
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 132 times.
134 if (ctx->num_to_add_fk) {
6597
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
6598
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 dict_foreign_free(ctx->add_fk[i]);
6599 }
6600 }
6601
6602
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 130 times.
134 if (ctx->num_to_drop_index) {
6603
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 row_mysql_lock_data_dictionary(prebuilt->trx, UT_LOCATION_HERE);
6604
6605 /* Clear the to_be_dropped flags
6606 in the data dictionary cache.
6607 The flags may already have been cleared,
6608 in case an error was detected in
6609 commit_inplace_alter_table(). */
6610
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
6611 4 dict_index_t *index = ctx->drop_index[i];
6612
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 assert(index->is_committed());
6613 4 index->to_be_dropped = 0;
6614 }
6615
6616
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 row_mysql_unlock_data_dictionary(prebuilt->trx);
6617 }
6618 }
6619
6620
1/2
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
393 reset_column_ord_part(prebuilt->table);
6621
6622 /* Do not commit/rollback prebuilt->trx, assume mysql will
6623 rollback it */
6624
6625 393 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
6626 393 return fail;
6627 393 }
6628
6629 /** Rename or enlarge columns in the data dictionary cache
6630 as part of commit_cache_norebuild().
6631 @param ha_alter_info Data used during in-place alter.
6632 @param table the TABLE
6633 @param user_table InnoDB table that was being altered */
6634 17016 static void innobase_rename_or_enlarge_columns_cache(
6635 Alter_inplace_info *ha_alter_info, const TABLE *table,
6636 dict_table_t *user_table) {
6637
2/2
✓ Branch 0 taken 16886 times.
✓ Branch 1 taken 130 times.
17016 if (!(ha_alter_info->handler_flags &
6638 (Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
6639 Alter_inplace_info::ALTER_COLUMN_NAME))) {
6640 16886 return;
6641 }
6642
6643 List_iterator_fast<Create_field> cf_it(
6644
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 ha_alter_info->alter_info->create_list);
6645 130 uint i = 0;
6646 130 ulint num_v = 0;
6647 130 ulint unsigned_flag = 0;
6648
6649
2/2
✓ Branch 0 taken 385 times.
✓ Branch 1 taken 130 times.
515 for (Field **fp = table->field; *fp; fp++, i++) {
6650
3/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 336 times.
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
385 bool is_virtual = innobase_is_v_fld(*fp);
6651
6652 385 cf_it.rewind();
6653
1/2
✓ Branch 0 taken 1420 times.
✗ Branch 1 not taken.
1420 while (const Create_field *cf = cf_it++) {
6654
2/2
✓ Branch 0 taken 1035 times.
✓ Branch 1 taken 385 times.
1420 if (cf->field != *fp) {
6655 1035 continue;
6656 }
6657
6658
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 336 times.
385 ulint col_n = is_virtual ? num_v : i - num_v;
6659
6660
3/4
✓ Branch 0 taken 385 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 295 times.
385 if ((*fp)->is_equal(cf) == IS_EQUAL_PACK_LENGTH) {
6661 dict_col_t *col;
6662
6663
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 89 times.
90 if (is_virtual) {
6664
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 col = &dict_table_get_nth_v_col(user_table, col_n)->m_col;
6665 } else {
6666
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 col = user_table->get_col(col_n);
6667 }
6668
1/2
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
90 col->len = cf->max_display_width_in_bytes();
6669
6670 ulint innodb_data_type =
6671
1/2
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
90 get_innobase_type_from_mysql_type(&unsigned_flag, cf->field);
6672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 ut_ad(innodb_data_type != DATA_MISSING);
6673
6674
5/6
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 46 times.
178 if (dtype_is_non_binary_string_type(innodb_data_type, col->prtype) &&
6675
3/4
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 44 times.
88 (*fp)->charset()->number != cf->charset->number) {
6676
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 ulint old_charset = (*fp)->charset()->number;
6677 44 ulint new_charset = cf->charset->number;
6678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 ut_ad(dtype_get_charset_coll(col->prtype) == old_charset);
6679
6680 44 col->prtype =
6681
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 dtype_form_prtype(col->prtype - (old_charset << 16), new_charset);
6682 ulint mbminlen;
6683 ulint mbmaxlen;
6684
6685
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 dtype_get_mblen(col->mtype, col->prtype, &mbminlen, &mbmaxlen);
6686 44 col->mbminmaxlen = DATA_MBMINMAXLEN(mbminlen, mbmaxlen);
6687 }
6688 }
6689
6690
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 342 times.
385 if ((*fp)->is_flag_set(FIELD_IS_RENAMED)) {
6691 43 dict_mem_table_col_rename(user_table, col_n, cf->field->field_name,
6692
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 cf->field_name, is_virtual);
6693 }
6694
6695 385 break;
6696 1035 }
6697
6698
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 336 times.
385 if (is_virtual) {
6699 49 num_v++;
6700 }
6701 }
6702 }
6703 /** Get the auto-increment value of the table on commit.
6704 @param[in] ha_alter_info Data used during in-place alter
6705 @param[in,out] ctx In-place ALTER TABLE context
6706 return autoinc value in ctx->max_autoinc
6707 @param[in] altered_table MySQL table that is being altered
6708 @param[in] old_table MySQL table as it is before the ALTER operation
6709 @retval true Failure
6710 @retval false Success*/
6711 47303 [[nodiscard]] static bool commit_get_autoinc(Alter_inplace_info *ha_alter_info,
6712 ha_innobase_inplace_ctx *ctx,
6713 const TABLE *altered_table,
6714 const TABLE *old_table) {
6715
1/2
✓ Branch 0 taken 47303 times.
✗ Branch 1 not taken.
47303 DBUG_TRACE;
6716
6717
2/2
✓ Branch 0 taken 42866 times.
✓ Branch 1 taken 4437 times.
47303 if (!altered_table->found_next_number_field) {
6718 /* There is no AUTO_INCREMENT column in the table
6719 after the ALTER operation. */
6720 42866 ctx->max_autoinc = 0;
6721
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 4396 times.
4437 } else if (ctx->add_autoinc != ULINT_UNDEFINED) {
6722 /* An AUTO_INCREMENT column was added. Get the last
6723 value from the sequence, which may be based on a
6724 supplied AUTO_INCREMENT value. */
6725 41 ctx->max_autoinc = ctx->sequence.last();
6726
2/2
✓ Branch 0 taken 1076 times.
✓ Branch 1 taken 3320 times.
4396 } else if ((ha_alter_info->handler_flags &
6727 1076 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
6728
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 932 times.
1076 (ha_alter_info->create_info->used_fields & HA_CREATE_USED_AUTO)) {
6729 /* Check if the table is discarded */
6730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (dict_table_is_discarded(ctx->old_table)) {
6731 return true;
6732 }
6733
6734 /* An AUTO_INCREMENT value was supplied, but the table was not
6735 rebuilt. Get the user-supplied value or the last value from the
6736 sequence. */
6737 uint64_t max_value_table;
6738
6739 144 Field *autoinc_field = old_table->found_next_number_field;
6740
6741 144 ctx->max_autoinc = ha_alter_info->create_info->auto_increment_value;
6742
6743
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 dict_table_autoinc_lock(ctx->old_table);
6744
6745 144 max_value_table = ctx->old_table->autoinc_persisted;
6746
6747 /* We still have to search the index here when we want to
6748 set the AUTO_INCREMENT value to a smaller or equal one.
6749
6750 Here is an example:
6751 Let's say we have a table t1 with one AUTOINC column, existing
6752 rows (1), (2), (100), (200), (1000), after following SQLs:
6753 DELETE FROM t1 WHERE a > 200;
6754 ALTER TABLE t1 AUTO_INCREMENT = 150;
6755 we expect the next value allocated from 201, but not 150.
6756
6757 We could only search the tree to know current max counter
6758 in the table and compare. */
6759
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 133 times.
144 if (ctx->max_autoinc <= max_value_table) {
6760 dberr_t err;
6761 dict_index_t *index;
6762
6763
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 index = dict_table_get_index_on_first_col(ctx->old_table,
6764 11 autoinc_field->field_index());
6765
6766
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 err = row_search_max_autoinc(index, autoinc_field->field_name,
6767 &max_value_table);
6768
6769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (err != DB_SUCCESS) {
6770 ctx->max_autoinc = 0;
6771 ut_d(ut_error);
6772
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 } else if (ctx->max_autoinc <= max_value_table) {
6773 ulonglong col_max_value;
6774 ulonglong offset;
6775
6776
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 col_max_value = autoinc_field->get_max_int_value();
6777 5 offset = ctx->prebuilt->autoinc_offset;
6778 5 ctx->max_autoinc =
6779
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 innobase_next_autoinc(max_value_table, 1, 1, offset, col_max_value);
6780 }
6781 }
6782
6783
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 dict_table_autoinc_unlock(ctx->old_table);
6784 144 } else {
6785 /* An AUTO_INCREMENT value was not specified.
6786 Read the old counter value from the table. */
6787
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4252 times.
4252 ut_ad(old_table->found_next_number_field);
6788
1/2
✓ Branch 0 taken 4252 times.
✗ Branch 1 not taken.
4252 dict_table_autoinc_lock(ctx->old_table);
6789 4252 ctx->max_autoinc = ctx->old_table->autoinc;
6790
1/2
✓ Branch 0 taken 4252 times.
✗ Branch 1 not taken.
4252 dict_table_autoinc_unlock(ctx->old_table);
6791 }
6792
6793 47303 return false;
6794 47303 }
6795
6796 /** Add or drop foreign key constraints to the data dictionary tables,
6797 but do not touch the data dictionary cache.
6798 @param ctx In-place ALTER TABLE context
6799 @param trx Data dictionary transaction
6800 @param table_name Table name in MySQL
6801 @retval true Failure
6802 @retval false Success
6803 */
6804 47302 [[nodiscard]] static bool innobase_update_foreign_try(
6805 ha_innobase_inplace_ctx *ctx, trx_t *trx, const char *table_name) {
6806 ulint foreign_id;
6807 ulint i;
6808
6809
1/2
✓ Branch 0 taken 47302 times.
✗ Branch 1 not taken.
47302 DBUG_TRACE;
6810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47302 times.
47302 assert(ctx);
6811
6812
1/2
✓ Branch 0 taken 47302 times.
✗ Branch 1 not taken.
47302 foreign_id = dict_table_get_highest_foreign_id(ctx->new_table);
6813
6814 47302 foreign_id++;
6815
6816
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 47300 times.
47396 for (i = 0; i < ctx->num_to_add_fk; i++) {
6817 96 dict_foreign_t *fk = ctx->add_fk[i];
6818
6819
4/6
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 88 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 96 times.
96 ut_ad(fk->foreign_table == ctx->new_table ||
6820 fk->foreign_table == ctx->old_table);
6821
6822 192 dberr_t error = dict_create_add_foreign_id(&foreign_id,
6823
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 ctx->old_table->name.m_name, fk);
6824
6825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (error != DB_SUCCESS) {
6826 my_error(ER_TOO_LONG_IDENT, MYF(0), fk->id);
6827 return true;
6828 }
6829
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 71 times.
96 if (!fk->foreign_index) {
6830 50 fk->foreign_index = dict_foreign_find_index(
6831 25 ctx->new_table, ctx->col_names, fk->foreign_col_names, fk->n_fields,
6832 25 fk->referenced_index, true,
6833
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 fk->type & (DICT_FOREIGN_ON_DELETE_SET_NULL |
6834 DICT_FOREIGN_ON_UPDATE_SET_NULL));
6835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (!fk->foreign_index) {
6836 my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_name, fk->id);
6837 return true;
6838 }
6839 }
6840
6841 /* During upgrade, inserts into SYS_* should be avoided. */
6842
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 if (!srv_is_upgrade_mode) {
6843
3/4
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 94 times.
96 DBUG_EXECUTE_IF("innodb_test_cannot_add_fk_system", error = DB_ERROR;);
6844
6845
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 94 times.
96 if (error != DB_SUCCESS) {
6846
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_error(ER_FK_FAIL_ADD_SYSTEM, MYF(0), fk->id);
6847 2 return true;
6848 }
6849 }
6850 }
6851
4/6
✓ Branch 0 taken 47300 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 47299 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
47300 DBUG_EXECUTE_IF("ib_drop_foreign_error",
6852 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
6853 trx->error_state = DB_SUCCESS; return true;);
6854 47299 return false;
6855 47302 }
6856
6857 /** Update the foreign key constraint definitions in the data dictionary cache
6858 after the changes to data dictionary tables were committed.
6859 @param[in,out] ctx In-place ALTER TABLE context
6860 @param[in] user_thd MySQL connection
6861 @param[in,out] dd_table dd table instance
6862 @return InnoDB error code (should always be DB_SUCCESS) */
6863 17032 [[nodiscard]] static dberr_t innobase_update_foreign_cache(
6864 ha_innobase_inplace_ctx *ctx, THD *user_thd, dd::Table *dd_table) {
6865 dict_table_t *user_table;
6866 17032 dberr_t err = DB_SUCCESS;
6867
6868
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 DBUG_TRACE;
6869
6870
2/4
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17032 times.
17032 ut_ad(dict_sys_mutex_own());
6871
6872 17032 user_table = ctx->old_table;
6873
6874 /* Discard the added foreign keys, because we will
6875 load them from the data dictionary. */
6876
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 17032 times.
17119 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
6877 87 dict_foreign_t *fk = ctx->add_fk[i];
6878
1/2
✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
87 dict_foreign_free(fk);
6879 }
6880
6881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17032 times.
17032 if (ctx->need_rebuild()) {
6882 /* The rebuilt table is already using the renamed
6883 column names. No need to pass col_names or to drop
6884 constraints from the data dictionary cache. */
6885 assert(!ctx->col_names);
6886 assert(user_table->foreign_set.empty());
6887 assert(user_table->referenced_set.empty());
6888 user_table = ctx->new_table;
6889 } else {
6890 /* Drop the foreign key constraints if the
6891 table was not rebuilt. If the table is rebuilt,
6892 there would not be any foreign key contraints for
6893 it yet in the data dictionary cache. */
6894
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 17032 times.
17070 for (ulint i = 0; i < ctx->num_to_drop_fk; i++) {
6895 38 dict_foreign_t *fk = ctx->drop_fk[i];
6896
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 dict_foreign_remove_from_cache(fk);
6897 }
6898 }
6899
6900 /* Load the old or added foreign keys from the data dictionary
6901 and prevent the table from being evicted from the data
6902 dictionary cache (work around the lack of WL#6049). */
6903
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 dict_names_t fk_tables;
6904
6905
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 dd::cache::Dictionary_client *client = dd::get_dd_client(user_thd);
6906
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6907 err =
6908
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 dd_table_load_fk(client, user_table->name.m_name, ctx->col_names,
6909 user_table, dd_table, user_thd, true, true, &fk_tables);
6910
6911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17032 times.
17032 if (err == DB_CANNOT_ADD_CONSTRAINT) {
6912 fk_tables.clear();
6913
6914 /* It is possible there are existing foreign key are
6915 loaded with "foreign_key checks" off,
6916 so let's retry the loading with charset_check is off */
6917 err = dd_table_load_fk(client, user_table->name.m_name, ctx->col_names,
6918 user_table, dd_table, user_thd, true, false,
6919 &fk_tables);
6920
6921 /* The load with "charset_check" off is successful, warn
6922 the user that the foreign key has loaded with mis-matched
6923 charset */
6924 if (err == DB_SUCCESS) {
6925 push_warning_printf(user_thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
6926 "Foreign key constraints for table '%s'"
6927 " are loaded with charset check off",
6928 user_table->name.m_name);
6929 }
6930 }
6931
6932 /* For complete loading of foreign keys, all associated tables must
6933 also be loaded. */
6934
6935
3/6
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17032 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 17032 times.
17032 while (err == DB_SUCCESS && !fk_tables.empty()) {
6936 dict_sys_mutex_exit();
6937 dd::cache::Dictionary_client *client = dd::get_dd_client(user_thd);
6938
6939 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6940
6941 dd_open_fk_tables(fk_tables, false, user_thd);
6942 dict_sys_mutex_enter();
6943 }
6944
6945 17032 return err;
6946 17032 }
6947
6948 /** Discard the foreign key cache if anyone is affected by current
6949 column rename. This is only used for rebuild case.
6950 @param[in] ha_alter_info data used during in-place alter
6951 @param[in] mysql_table MySQL TABLE object
6952 @param[in,out] old_table InnoDB table object for old table */
6953 14 static void innobase_rename_col_discard_foreign(
6954 Alter_inplace_info *ha_alter_info, const TABLE *mysql_table,
6955 dict_table_t *old_table) {
6956 List_iterator_fast<Create_field> cf_it(
6957
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 ha_alter_info->alter_info->create_list);
6958
6959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 ut_ad(ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME);
6960
6961
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 14 times.
61 for (Field **fp = mysql_table->field; *fp; fp++) {
6962
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 17 times.
47 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
6963 30 continue;
6964 }
6965
6966 17 cf_it.rewind();
6967
6968 17 ut_d(bool processed = false;)
6969
6970
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 17 times.
81 while (Create_field *cf = cf_it++) {
6971
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 17 times.
64 if (cf->field != *fp) {
6972 47 continue;
6973 }
6974
6975 /* Now cf->field->field_name is the old name, check the foreign key
6976 information to see any one gets affected by this rename, and discard
6977 them from cache */
6978
6979 17 std::list<dict_foreign_t *> fk_evict;
6980
6981
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 17 times.
26 for (auto fk : old_table->foreign_set) {
6982 9 dict_foreign_t *foreign = fk;
6983
6984
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
15 for (unsigned i = 0; i < foreign->n_fields; i++) {
6985
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strcmp(foreign->foreign_col_names[i], cf->field->field_name) !=
6986 0) {
6987 6 continue;
6988 }
6989
6990
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 fk_evict.push_back(foreign);
6991 3 break;
6992 }
6993 }
6994
6995
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 for (auto fk : old_table->referenced_set) {
6996 1 dict_foreign_t *foreign = fk;
6997
6998
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 for (unsigned i = 0; i < foreign->n_fields; i++) {
6999
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (strcmp(foreign->referenced_col_names[i], cf->field->field_name) !=
7000 0) {
7001 continue;
7002 }
7003
7004
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fk_evict.push_back(foreign);
7005 1 break;
7006 }
7007 }
7008
7009
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 std::for_each(fk_evict.begin(), fk_evict.end(),
7010 dict_foreign_remove_from_cache);
7011
7012 17 ut_d(processed = true;)
7013 81 }
7014
7015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 ut_ad(processed);
7016 }
7017 14 }
7018
7019 /** Commit the changes made during prepare_inplace_alter_table()
7020 and inplace_alter_table() inside the data dictionary tables,
7021 when rebuilding the table.
7022 @param ha_alter_info Data used during in-place alter
7023 @param ctx In-place ALTER TABLE context
7024 @param altered_table MySQL table that is being altered
7025 @param old_table MySQL table as it is before the ALTER operation
7026 @param trx Data dictionary transaction
7027 @param table_name Table name in MySQL
7028 @retval true Failure
7029 @retval false Success
7030 */
7031 30252 [[nodiscard]] inline bool commit_try_rebuild(Alter_inplace_info *ha_alter_info,
7032 ha_innobase_inplace_ctx *ctx,
7033 TABLE *altered_table,
7034 const TABLE *old_table, trx_t *trx,
7035 const char *table_name) {
7036 30252 dict_table_t *rebuilt_table = ctx->new_table;
7037 30252 dict_table_t *user_table = ctx->old_table;
7038
7039
1/2
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
30252 DBUG_TRACE;
7040
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30252 times.
30252 assert(ctx->need_rebuild());
7041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30252 times.
30252 assert(trx->dict_operation_lock_mode == RW_X_LATCH);
7042
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 30252 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
30252 assert(
7043 !(ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) ||
7044 ctx->num_to_drop_fk > 0);
7045
7046
3/4
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39652 times.
✓ Branch 3 taken 30252 times.
69904 for (dict_index_t *index = rebuilt_table->first_index(); index;
7047
1/2
✓ Branch 0 taken 39652 times.
✗ Branch 1 not taken.
39652 index = index->next()) {
7048
2/4
✓ Branch 0 taken 39652 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39652 times.
39652 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7049
2/4
✓ Branch 0 taken 39652 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39652 times.
39652 assert(index->is_committed());
7050
2/4
✓ Branch 0 taken 39652 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39652 times.
39652 if (index->is_corrupted()) {
7051 my_error(ER_INDEX_CORRUPT, MYF(0), index->name());
7052 return true;
7053 }
7054 }
7055
7056
3/4
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30251 times.
30252 if (innobase_update_foreign_try(ctx, trx, table_name)) {
7057 1 return true;
7058 }
7059
7060 30251 dberr_t error = DB_SUCCESS;
7061
7062 /* Clear the to_be_dropped flag in the data dictionary cache
7063 of user_table. */
7064
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 30251 times.
30321 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7065 70 dict_index_t *index = ctx->drop_index[i];
7066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 assert(index->table == user_table);
7067
2/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70 times.
70 assert(index->is_committed());
7068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 assert(index->to_be_dropped);
7069 70 index->to_be_dropped = 0;
7070 }
7071
7072 /* We copied the table. Any indexes that were requested to be
7073 dropped were not created in the copy of the table. Apply any
7074 last bit of the rebuild log and then rename the tables. */
7075
7076
2/2
✓ Branch 0 taken 29966 times.
✓ Branch 1 taken 285 times.
30251 if (ctx->online) {
7077
3/4
✓ Branch 0 taken 29127 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 29127 times.
✗ Branch 3 not taken.
29966 DEBUG_SYNC_C("row_log_table_apply2_before");
7078
7079 29966 dict_vcol_templ_t *s_templ = nullptr;
7080
7081
2/2
✓ Branch 0 taken 820 times.
✓ Branch 1 taken 29146 times.
29966 if (ctx->new_table->n_v_cols > 0) {
7082
1/2
✓ Branch 0 taken 820 times.
✗ Branch 1 not taken.
820 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
7083 820 s_templ->vtempl = nullptr;
7084
7085
1/2
✓ Branch 0 taken 820 times.
✗ Branch 1 not taken.
820 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, nullptr,
7086 true, nullptr);
7087 820 ctx->new_table->vc_templ = s_templ;
7088 }
7089
7090 59932 error = row_log_table_apply(
7091 ctx->thr, user_table, altered_table,
7092
1/2
✓ Branch 0 taken 29966 times.
✗ Branch 1 not taken.
29966 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx)
7093 ->m_stage);
7094
7095
2/2
✓ Branch 0 taken 820 times.
✓ Branch 1 taken 29146 times.
29966 if (s_templ) {
7096
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 820 times.
820 ut_ad(ctx->need_rebuild());
7097
1/2
✓ Branch 0 taken 820 times.
✗ Branch 1 not taken.
820 dict_free_vc_templ(s_templ);
7098 820 ut::delete_(s_templ);
7099 820 ctx->new_table->vc_templ = nullptr;
7100 }
7101
7102
1/2
✓ Branch 0 taken 29966 times.
✗ Branch 1 not taken.
29966 ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
7103
7104
1/5
✓ Branch 0 taken 29966 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
29966 switch (error) {
7105 KEY *dup_key;
7106 29966 case DB_SUCCESS:
7107 29966 break;
7108 case DB_DUPLICATE_KEY:
7109 if (err_key == ULINT_UNDEFINED) {
7110 /* This should be the hidden index on
7111 FTS_DOC_ID. */
7112 dup_key = nullptr;
7113 } else {
7114 /* Check if there is generated cluster index column */
7115 if (ctx->num_to_add_index > ha_alter_info->key_count) {
7116 assert(err_key <= ha_alter_info->key_count);
7117 dup_key = &ha_alter_info->key_info_buffer[err_key - 1];
7118 } else {
7119 assert(err_key < ha_alter_info->key_count);
7120 dup_key = &ha_alter_info->key_info_buffer[err_key];
7121 }
7122 }
7123 print_keydup_error(altered_table, dup_key, MYF(0),
7124 old_table->s->table_name.str);
7125 return true;
7126 case DB_ONLINE_LOG_TOO_BIG:
7127 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
7128 get_error_key_name(err_key, ha_alter_info, rebuilt_table));
7129 return true;
7130 case DB_INDEX_CORRUPT:
7131 my_error(ER_INDEX_CORRUPT, MYF(0),
7132 get_error_key_name(err_key, ha_alter_info, rebuilt_table));
7133 return true;
7134 default:
7135 my_error_innodb(error, table_name, user_table->flags);
7136 return true;
7137 }
7138 }
7139
2/6
✓ Branch 0 taken 30251 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30251 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30251 DBUG_EXECUTE_IF("ib_rename_column_error",
7140 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7141 trx->error_state = DB_SUCCESS; trx->op_info = "";
7142 return true;);
7143
2/6
✓ Branch 0 taken 30251 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30251 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30251 DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
7144
7145 /* The new table must inherit the flag from the
7146 "parent" table. */
7147
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 30247 times.
30251 if (dict_table_is_discarded(user_table)) {
7148 4 rebuilt_table->ibd_file_missing = true;
7149 4 rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
7150 }
7151 /* We must be still holding a table handle. */
7152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30251 times.
30251 assert(user_table->get_ref_count() >= 1);
7153
7154
2/6
✓ Branch 0 taken 30251 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30251 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30251 DBUG_EXECUTE_IF("ib_ddl_crash_after_rename", DBUG_SUICIDE(););
7155
2/4
✓ Branch 0 taken 30251 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30251 times.
30251 DBUG_EXECUTE_IF("ib_rebuild_cannot_rename", error = DB_ERROR;);
7156
7157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30251 times.
30251 if (user_table->get_ref_count() > 1) {
7158 /* This should only occur when an innodb_memcached
7159 connection with innodb_api_enable_mdl=off was started
7160 before commit_inplace_alter_table() locked the data
7161 dictionary. We must roll back the ALTER TABLE, because
7162 we cannot drop a table while it is being used. */
7163
7164 /* Normally, n_ref_count must be 1, because purge
7165 cannot be executing on this very table as we are
7166 holding MDL lock. */
7167 my_error(ER_TABLE_REFERENCED, MYF(0));
7168 return true;
7169 }
7170
7171
1/4
✓ Branch 0 taken 30251 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
30251 switch (error) {
7172 30251 case DB_SUCCESS:
7173 30251 return false;
7174 case DB_TABLESPACE_EXISTS:
7175 ut_a(rebuilt_table->get_ref_count() == 1);
7176 my_error(ER_TABLESPACE_EXISTS, MYF(0), ctx->tmp_name);
7177 return true;
7178 case DB_DUPLICATE_KEY:
7179 ut_a(rebuilt_table->get_ref_count() == 1);
7180 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), ctx->tmp_name);
7181 return true;
7182 default:
7183 my_error_innodb(error, table_name, user_table->flags);
7184 return true;
7185 }
7186 30252 }
7187
7188 /** Apply the changes made during commit_try_rebuild(),
7189 to the data dictionary cache and the file system.
7190 @param ctx In-place ALTER TABLE context */
7191 30208 inline void commit_cache_rebuild(ha_innobase_inplace_ctx *ctx) {
7192 dberr_t error;
7193
7194
1/2
✓ Branch 0 taken 30208 times.
✗ Branch 1 not taken.
30208 DBUG_TRACE;
7195
3/4
✓ Branch 0 taken 29369 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 29369 times.
✗ Branch 3 not taken.
30208 DEBUG_SYNC_C("commit_cache_rebuild");
7196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30208 times.
30208 assert(ctx->need_rebuild());
7197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30208 times.
30208 assert(dict_table_is_discarded(ctx->old_table) ==
7198 dict_table_is_discarded(ctx->new_table));
7199
7200 const char *old_name =
7201
1/2
✓ Branch 0 taken 30208 times.
✗ Branch 1 not taken.
30208 mem_heap_strdup(ctx->heap, ctx->old_table->name.m_name);
7202
7203 /* We already committed and redo logged the renames,
7204 so this must succeed. */
7205
1/2
✓ Branch 0 taken 30184 times.
✗ Branch 1 not taken.
30208 error = dict_table_rename_in_cache(ctx->old_table, ctx->tmp_name, false);
7206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30184 times.
30184 ut_a(error == DB_SUCCESS);
7207
7208
3/4
✓ Branch 0 taken 29345 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 29345 times.
✗ Branch 3 not taken.
30184 DEBUG_SYNC_C("commit_cache_rebuild_middle");
7209
7210
1/2
✓ Branch 0 taken 30160 times.
✗ Branch 1 not taken.
30184 error = dict_table_rename_in_cache(ctx->new_table, old_name, false);
7211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30160 times.
30160 ut_a(error == DB_SUCCESS);
7212 30160 }
7213
7214 /** Set of column numbers */
7215 typedef std::set<ulint, std::less<ulint>, ut::allocator<ulint>> col_set;
7216
7217 /** Store the column number of the columns in a list belonging
7218 to indexes which are not being dropped.
7219 @param[in] ctx In-place ALTER TABLE context
7220 @param[in, out] drop_col_list list which will be set, containing columns
7221 which is part of index being dropped
7222 @param[in, out] drop_v_col_list list which will be set, containing
7223 virtual columns which is part of index
7224 being dropped */
7225 17032 static void get_col_list_to_be_dropped(const ha_innobase_inplace_ctx *ctx,
7226 col_set &drop_col_list,
7227 col_set &drop_v_col_list) {
7228
2/2
✓ Branch 0 taken 1182 times.
✓ Branch 1 taken 17032 times.
18214 for (ulint index_count = 0; index_count < ctx->num_to_drop_index;
7229 index_count++) {
7230 1182 const dict_index_t *index = ctx->drop_index[index_count];
7231
7232
2/2
✓ Branch 0 taken 1316 times.
✓ Branch 1 taken 1182 times.
2498 for (ulint col = 0; col < index->n_user_defined_cols; col++) {
7233 1316 const dict_col_t *idx_col = index->get_col(col);
7234
7235
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 1200 times.
1316 if (idx_col->is_virtual()) {
7236 116 const dict_v_col_t *v_col =
7237 reinterpret_cast<const dict_v_col_t *>(idx_col);
7238 116 drop_v_col_list.insert(v_col->v_pos);
7239
7240 } else {
7241
1/2
✓ Branch 0 taken 1200 times.
✗ Branch 1 not taken.
1200 ulint col_no = dict_col_get_no(idx_col);
7242
1/2
✓ Branch 0 taken 1200 times.
✗ Branch 1 not taken.
1200 drop_col_list.insert(col_no);
7243 }
7244 }
7245 }
7246 17032 }
7247
7248 /** Commit the changes made during prepare_inplace_alter_table() and
7249 inplace_alter_table() inside the data dictionary tables, when not rebuilding
7250 the table.
7251 @param[in] ha_alter_info Data used during in-place alter
7252 @param[in] ctx In-place ALTER TABLE context
7253 @param[in] trx Data dictionary transaction
7254 @param[in] table_name Table name in MySQL
7255 @retval true Failure
7256 @retval false Success */
7257 17051 [[nodiscard]] inline bool commit_try_norebuild(
7258 Alter_inplace_info *ha_alter_info, ha_innobase_inplace_ctx *ctx, trx_t *trx,
7259 const char *table_name) {
7260
1/2
✓ Branch 0 taken 17051 times.
✗ Branch 1 not taken.
17051 DBUG_TRACE;
7261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17051 times.
17051 assert(!ctx->need_rebuild());
7262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17051 times.
17051 assert(trx->dict_operation_lock_mode == RW_X_LATCH);
7263
3/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 17013 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
17051 assert(
7264 !(ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) ||
7265 ctx->num_to_drop_fk > 0);
7266
7267
2/2
✓ Branch 0 taken 5322 times.
✓ Branch 1 taken 17050 times.
22372 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7268 5322 dict_index_t *index = ctx->add_index[i];
7269
2/4
✓ Branch 0 taken 5322 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5322 times.
5322 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7270
2/4
✓ Branch 0 taken 5322 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5322 times.
5322 assert(!index->is_committed());
7271
3/4
✓ Branch 0 taken 5322 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5321 times.
5322 if (index->is_corrupted()) {
7272 /* Report a duplicate key
7273 error for the index that was
7274 flagged corrupted, most likely
7275 because a duplicate value was
7276 inserted (directly or by
7277 rollback) after
7278 ha_innobase::inplace_alter_table()
7279 completed.
7280 TODO: report this as a corruption
7281 with a detailed reason once
7282 WL#6379 has been implemented. */
7283
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_DUP_UNKNOWN_IN_INDEX, MYF(0), index->name());
7284 1 return true;
7285 }
7286 }
7287
7288
3/4
✓ Branch 0 taken 17050 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 17048 times.
17050 if (innobase_update_foreign_try(ctx, trx, table_name)) {
7289 2 return true;
7290 }
7291
7292
4/6
✓ Branch 0 taken 17048 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 17046 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
17048 DBUG_EXECUTE_IF("ib_rename_column_error",
7293 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7294 trx->error_state = DB_SUCCESS; trx->op_info = "";
7295 return true;);
7296
7297
4/6
✓ Branch 0 taken 17046 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 17045 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
17046 DBUG_EXECUTE_IF("ib_resize_column_error",
7298 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7299 trx->error_state = DB_SUCCESS; trx->op_info = "";
7300 return true;);
7301
7302
4/6
✓ Branch 0 taken 17045 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 17044 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
17045 DBUG_EXECUTE_IF(
7303 "ib_rename_index_fail1", my_error_innodb(DB_DEADLOCK, table_name, 0);
7304 trx->error_state = DB_SUCCESS; trx->op_info = ""; return true;);
7305
7306 17044 return false;
7307 17051 }
7308
7309 /** Commit the changes to the data dictionary cache
7310 after a successful commit_try_norebuild() call.
7311 @param ctx In-place ALTER TABLE context
7312 @param trx Data dictionary transaction object
7313 (will be started and committed)
7314 @return whether all replacements were found for dropped indexes */
7315 17032 [[nodiscard]] inline bool commit_cache_norebuild(ha_innobase_inplace_ctx *ctx,
7316 trx_t *trx) {
7317
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 DBUG_TRACE;
7318
7319 17032 bool found = true;
7320
7321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17032 times.
17032 assert(!ctx->need_rebuild());
7322
7323
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 col_set drop_list;
7324
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 col_set v_drop_list;
7325 17032 col_set::const_iterator col_it;
7326
7327 /* Check if the column, part of an index to be dropped is part of any
7328 other index which is not being dropped. If not, then set the ord_part
7329 of the column to 0. Here the columns are collected first. */
7330
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 get_col_list_to_be_dropped(ctx, drop_list, v_drop_list);
7331
7332
2/2
✓ Branch 0 taken 5308 times.
✓ Branch 1 taken 17032 times.
22340 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7333 5308 dict_index_t *index = ctx->add_index[i];
7334
2/4
✓ Branch 0 taken 5308 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5308 times.
5308 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7335
2/4
✓ Branch 0 taken 5308 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5308 times.
5308 assert(!index->is_committed());
7336
1/2
✓ Branch 0 taken 5308 times.
✗ Branch 1 not taken.
5308 index->set_committed(true);
7337 }
7338
7339
2/2
✓ Branch 0 taken 1138 times.
✓ Branch 1 taken 15894 times.
17032 if (ctx->num_to_drop_index) {
7340 /* Drop indexes in data dictionary cache and write
7341 DDL log for them */
7342
2/2
✓ Branch 0 taken 1182 times.
✓ Branch 1 taken 1138 times.
2320 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7343 1182 dict_index_t *index = ctx->drop_index[i];
7344
2/4
✓ Branch 0 taken 1182 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1182 times.
1182 assert(index->is_committed());
7345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1182 times.
1182 assert(index->table == ctx->new_table);
7346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1182 times.
1182 assert(index->to_be_dropped);
7347
7348 /* Replace the indexes in foreign key
7349 constraints if needed. */
7350
2/4
✓ Branch 0 taken 1182 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1182 times.
1182 if (!dict_foreign_replace_index(index->table, ctx->col_names, index)) {
7351 found = false;
7352 }
7353 }
7354
7355
2/2
✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 1122 times.
2302 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7356 1180 dict_index_t *index = ctx->drop_index[i];
7357
2/4
✓ Branch 0 taken 1180 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1180 times.
1180 assert(index->is_committed());
7358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1180 times.
1180 assert(index->table == ctx->new_table);
7359
7360
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 1066 times.
1180 if (index->type & DICT_FTS) {
7361
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
114 assert(index->type == DICT_FTS || index->is_corrupted());
7362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 assert(index->table->fts);
7363
1/2
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
114 ctx->fts_drop_aux_vec = new aux_name_vec_t;
7364
1/2
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
114 fts_drop_index(index->table, index, trx, ctx->fts_drop_aux_vec);
7365 }
7366
7367 /* It is a single table tablespace and the .ibd file is
7368 missing if root is FIL_NULL, do nothing. */
7369
2/2
✓ Branch 0 taken 1063 times.
✓ Branch 1 taken 117 times.
1180 if (index->page != FIL_NULL) {
7370
1/2
✓ Branch 0 taken 1063 times.
✗ Branch 1 not taken.
1063 dict_sys_mutex_exit();
7371
1/2
✓ Branch 0 taken 1047 times.
✗ Branch 1 not taken.
1063 ut_d(dberr_t err =) log_ddl->write_free_tree_log(trx, index, true);
7372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 ut_ad(err == DB_SUCCESS);
7373
1/2
✓ Branch 0 taken 1047 times.
✗ Branch 1 not taken.
1047 dict_sys_mutex_enter();
7374 }
7375
7376
1/2
✓ Branch 0 taken 1164 times.
✗ Branch 1 not taken.
1164 btr_drop_ahi_for_index(index);
7377
1/2
✓ Branch 0 taken 1164 times.
✗ Branch 1 not taken.
1164 dict_index_remove_from_cache(index->table, index);
7378 }
7379 }
7380
7381 /* Update the ord_part after index dropped, to get accurate values */
7382
2/2
✓ Branch 0 taken 1169 times.
✓ Branch 1 taken 17016 times.
18185 for (col_it = drop_list.begin(); col_it != drop_list.end(); ++col_it) {
7383
3/4
✓ Branch 0 taken 1169 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 917 times.
✓ Branch 3 taken 252 times.
1169 if (!check_col_exists_in_indexes(ctx->new_table, *col_it, false)) {
7384 917 ctx->new_table->cols[*col_it].ord_part = 0;
7385 }
7386 }
7387
7388
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 17016 times.
17127 for (col_it = v_drop_list.begin(); col_it != v_drop_list.end(); ++col_it) {
7389
3/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✓ Branch 3 taken 9 times.
111 if (!check_col_exists_in_indexes(ctx->new_table, *col_it, true)) {
7390 102 ctx->new_table->v_cols[*col_it].m_col.ord_part = 0;
7391 }
7392 }
7393
7394 17016 ctx->new_table->fts_doc_id_index =
7395 17016 ctx->new_table->fts
7396
3/4
✓ Branch 0 taken 468 times.
✓ Branch 1 taken 16548 times.
✓ Branch 2 taken 468 times.
✗ Branch 3 not taken.
17016 ? dict_table_get_index_on_name(ctx->new_table, FTS_DOC_ID_INDEX_NAME)
7397 : nullptr;
7398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17016 times.
17016 assert((ctx->new_table->fts == nullptr) ==
7399 (ctx->new_table->fts_doc_id_index == nullptr));
7400
7401 17016 return found;
7402 17016 }
7403
7404 /** Adjust the persistent statistics after non-rebuilding ALTER TABLE.
7405 Remove statistics for dropped indexes, add statistics for created indexes
7406 and rename statistics for renamed indexes.
7407 @param ha_alter_info Data used during in-place alter
7408 @param ctx In-place ALTER TABLE context
7409 @param table_name Table name in MySQL
7410 @param thd MySQL connection
7411 */
7412 16623 static void alter_stats_norebuild(Alter_inplace_info *ha_alter_info,
7413 ha_innobase_inplace_ctx *ctx,
7414 const char *table_name, THD *thd) {
7415 ulint i;
7416
7417
1/2
✓ Branch 0 taken 16623 times.
✗ Branch 1 not taken.
16623 DBUG_TRACE;
7418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16623 times.
16623 assert(!ctx->need_rebuild());
7419
7420
3/4
✓ Branch 0 taken 16623 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4706 times.
✓ Branch 3 taken 11917 times.
16623 if (!dict_stats_is_persistent_enabled(ctx->new_table)) {
7421 4706 return;
7422 }
7423
7424 /* Delete corresponding rows from the stats table. We do this
7425 in a separate transaction from trx, because lock waits are not
7426 allowed in a data dictionary transaction. (Lock waits are possible
7427 on the statistics table, because it is directly accessible by users,
7428 not covered by the dict_operation_lock.)
7429
7430 Because the data dictionary changes were already committed, orphaned
7431 rows may be left in the statistics table if the system crashes.
7432
7433 FIXME: each change to the statistics tables is being committed in a
7434 separate transaction, meaning that the operation is not atomic
7435
7436 FIXME: This will not drop the (unused) statistics for
7437 FTS_DOC_ID_INDEX if it was a hidden index, dropped together
7438 with the last renamining FULLTEXT index. */
7439
2/2
✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 11917 times.
12949 for (i = 0; i < ha_alter_info->index_drop_count; i++) {
7440 1032 const KEY *key = ha_alter_info->index_drop_buffer[i];
7441
7442
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 920 times.
1032 if (key->flags & HA_FULLTEXT) {
7443 /* There are no index cardinality
7444 statistics for FULLTEXT indexes. */
7445 112 continue;
7446 }
7447
7448 char errstr[ERROR_STR_LENGTH];
7449
7450
1/2
✓ Branch 0 taken 920 times.
✗ Branch 1 not taken.
920 if (dict_stats_drop_index(ctx->new_table->name.m_name, key->name, errstr,
7451
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 919 times.
920 sizeof errstr) != DB_SUCCESS) {
7452
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 push_warning(thd, Sql_condition::SL_WARNING, ER_LOCK_WAIT_TIMEOUT,
7453 errstr);
7454 }
7455 }
7456
7457
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 11917 times.
11995 for (i = 0; i < ha_alter_info->index_rename_count; i++) {
7458 78 KEY_PAIR *pair = &ha_alter_info->index_rename_buffer[i];
7459 dberr_t err;
7460
7461 156 err = dict_stats_rename_index(ctx->new_table, pair->old_key->name,
7462
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 pair->new_key->name);
7463
7464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (err != DB_SUCCESS) {
7465 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ERROR_ON_RENAME,
7466 "Error renaming an index of table '%s'"
7467 " from '%s' to '%s' in InnoDB persistent"
7468 " statistics storage: %s",
7469 table_name, pair->old_key->name, pair->new_key->name,
7470 ut_strerr(err));
7471 }
7472 }
7473
7474
2/2
✓ Branch 0 taken 5076 times.
✓ Branch 1 taken 11917 times.
16993 for (i = 0; i < ctx->num_to_add_index; i++) {
7475 5076 dict_index_t *index = ctx->add_index[i];
7476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5076 times.
5076 assert(index->table == ctx->new_table);
7477
7478
2/2
✓ Branch 0 taken 4749 times.
✓ Branch 1 taken 327 times.
5076 if (!(index->type & DICT_FTS)) {
7479
1/2
✓ Branch 0 taken 4749 times.
✗ Branch 1 not taken.
4749 dict_stats_init(ctx->new_table);
7480
1/2
✓ Branch 0 taken 4749 times.
✗ Branch 1 not taken.
4749 dict_stats_update_for_index(index);
7481 }
7482 }
7483
2/2
✓ Branch 0 taken 11917 times.
✓ Branch 1 taken 4706 times.
16623 }
7484
7485 /** Adjust the persistent statistics after rebuilding ALTER TABLE.
7486 Remove statistics for dropped indexes, add statistics for created indexes
7487 and rename statistics for renamed indexes.
7488 @param table InnoDB table that was rebuilt by ALTER TABLE
7489 @param table_name Table name in MySQL
7490 @param thd MySQL connection
7491 */
7492 30071 static void alter_stats_rebuild(dict_table_t *table, const char *table_name,
7493 THD *thd) {
7494
1/2
✓ Branch 0 taken 30071 times.
✗ Branch 1 not taken.
30071 DBUG_TRACE;
7495
2/6
✓ Branch 0 taken 30071 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30071 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30071 DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
7496
7497
4/4
✓ Branch 0 taken 30067 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 16004 times.
✓ Branch 3 taken 14067 times.
60138 if (dict_table_is_discarded(table) ||
7498
3/4
✓ Branch 0 taken 30067 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16000 times.
✓ Branch 3 taken 14067 times.
30067 !dict_stats_is_persistent_enabled(table)) {
7499 16004 return;
7500 }
7501
7502 #ifdef UNIV_DEBUG
7503 14067 bool ibd_file_missing_orig = false;
7504 #endif /* UNIV_DEBUG */
7505
7506
3/4
✓ Branch 0 taken 14067 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14066 times.
14067 DBUG_EXECUTE_IF("ib_rename_index_fail2",
7507 ibd_file_missing_orig = table->ibd_file_missing;
7508 table->ibd_file_missing = true;);
7509
7510
1/2
✓ Branch 0 taken 14067 times.
✗ Branch 1 not taken.
14067 dberr_t ret = dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
7511
7512
3/4
✓ Branch 0 taken 14067 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14066 times.
14067 DBUG_EXECUTE_IF("ib_rename_index_fail2",
7513 table->ibd_file_missing = ibd_file_missing_orig;);
7514
7515
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14066 times.
14067 if (ret != DB_SUCCESS) {
7516
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
7517 "Error updating stats for table '%s'"
7518 " after table rebuild: %s",
7519 table_name, ut_strerr(ret));
7520 }
7521
2/2
✓ Branch 0 taken 14067 times.
✓ Branch 1 taken 16004 times.
30071 }
7522
7523 /** Implementation of commit_inplace_alter_table()
7524 @tparam Table dd::Table or dd::Partition
7525 @param[in] altered_table TABLE object for new version of table.
7526 @param[in,out] ha_alter_info Structure describing changes to be done
7527 by ALTER TABLE and holding data used
7528 during in-place alter.
7529 @param[in] commit True to commit or false to rollback.
7530 @param[in,out] new_dd_tab Table object for the new version of the
7531 table. Can be adjusted by this call.
7532 Changes to the table definition
7533 will be persisted in the data-dictionary
7534 at statement version of it.
7535 @retval true Failure
7536 @retval false Success */
7537 template <typename Table>
7538 53113 bool ha_innobase::commit_inplace_alter_table_impl(
7539 TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit,
7540 Table *new_dd_tab) {
7541 dberr_t error;
7542 ha_innobase_inplace_ctx *ctx0;
7543
1/2
✓ Branch 0 taken 53113 times.
✗ Branch 1 not taken.
53113 struct mtr_buf_copy_t logs;
7544
7545 53113 ctx0 = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
7546
7547 #ifdef UNIV_DEBUG
7548 53113 uint crash_inject_count = 1;
7549 53113 uint crash_fail_inject_count = 1;
7550 53113 uint failure_inject_count = 1;
7551 #endif /* UNIV_DEBUG */
7552
7553
1/2
✓ Branch 0 taken 53113 times.
✗ Branch 1 not taken.
53113 DBUG_TRACE;
7554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53113 times.
53113 assert(!srv_read_only_mode);
7555
3/4
✓ Branch 0 taken 45079 times.
✓ Branch 1 taken 8034 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45079 times.
53113 assert(!ctx0 || ctx0->prebuilt == m_prebuilt);
7556
3/4
✓ Branch 0 taken 45079 times.
✓ Branch 1 taken 8034 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45079 times.
53113 assert(!ctx0 || ctx0->old_table == m_prebuilt->table);
7557
7558
3/4
✓ Branch 0 taken 51451 times.
✓ Branch 1 taken 1662 times.
✓ Branch 2 taken 51451 times.
✗ Branch 3 not taken.
53113 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_enter");
7559
7560
3/4
✓ Branch 0 taken 51451 times.
✓ Branch 1 taken 1662 times.
✓ Branch 2 taken 51451 times.
✗ Branch 3 not taken.
53113 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_wait");
7561
7562
4/4
✓ Branch 0 taken 45079 times.
✓ Branch 1 taken 8034 times.
✓ Branch 2 taken 33208 times.
✓ Branch 3 taken 11871 times.
53113 if (ctx0 != nullptr && ctx0->m_stage != nullptr) {
7563
1/2
✓ Branch 0 taken 33208 times.
✗ Branch 1 not taken.
33208 ctx0->m_stage->begin_phase_end();
7564 }
7565
7566
2/2
✓ Branch 0 taken 393 times.
✓ Branch 1 taken 52720 times.
53113 if (!commit) {
7567 /* A rollback is being requested. So far we may at
7568 most have created some indexes. If any indexes were to
7569 be dropped, they would actually be dropped in this
7570 method if commit=true. */
7571 const bool ret =
7572
1/2
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
393 rollback_inplace_alter_table(ha_alter_info, table, m_prebuilt);
7573 393 return ret;
7574 }
7575
7576
6/6
✓ Branch 0 taken 46068 times.
✓ Branch 1 taken 6652 times.
✓ Branch 2 taken 1123 times.
✓ Branch 3 taken 44945 times.
✓ Branch 4 taken 7775 times.
✓ Branch 5 taken 44945 times.
98788 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
7577 46068 is_instant(ha_alter_info)) {
7578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7775 times.
7775 assert(!ctx0);
7579 7775 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
7580 7775 ha_alter_info->group_commit_ctx = nullptr;
7581 7775 return false;
7582 }
7583
7584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44945 times.
44945 assert(ctx0);
7585
7586 inplace_alter_handler_ctx **ctx_array;
7587 inplace_alter_handler_ctx *ctx_single[2];
7588
7589
2/2
✓ Branch 0 taken 698 times.
✓ Branch 1 taken 44247 times.
44945 if (ha_alter_info->group_commit_ctx) {
7590 698 ctx_array = ha_alter_info->group_commit_ctx;
7591 } else {
7592 44247 ctx_single[0] = ctx0;
7593 44247 ctx_single[1] = nullptr;
7594 44247 ctx_array = ctx_single;
7595 }
7596
7597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44945 times.
44945 assert(ctx0 == ctx_array[0]);
7598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44945 times.
44945 ut_ad(m_prebuilt->table == ctx0->old_table);
7599 44945 ha_alter_info->group_commit_ctx = nullptr;
7600
7601
1/2
✓ Branch 0 taken 44945 times.
✗ Branch 1 not taken.
44945 trx_start_if_not_started_xa(m_prebuilt->trx, true, UT_LOCATION_HERE);
7602
7603
2/2
✓ Branch 0 taken 47303 times.
✓ Branch 1 taken 44945 times.
92248 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7604 47303 ha_innobase_inplace_ctx *ctx =
7605 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47303 times.
47303 assert(ctx->prebuilt->trx == m_prebuilt->trx);
7607
7608 /* Exclusively lock the table, to ensure that no other
7609 transaction is holding locks on the table while we
7610 change the table definition. The MySQL meta-data lock
7611 should normally guarantee that no conflicting locks
7612 exist. However, FOREIGN KEY constraints checks and any
7613 transactions collected during crash recovery could be
7614 holding InnoDB locks only, not MySQL locks. */
7615
7616 47303 error = ddl::lock_table(m_prebuilt->trx, ctx->old_table, LOCK_X);
7617
7618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47303 times.
47303 if (error != DB_SUCCESS) {
7619 my_error_innodb(error, table_share->table_name.str, 0);
7620 return true;
7621 }
7622 }
7623
7624
3/4
✓ Branch 0 taken 43586 times.
✓ Branch 1 taken 1359 times.
✓ Branch 2 taken 43586 times.
✗ Branch 3 not taken.
44945 DEBUG_SYNC(m_user_thd, "innodb_alter_commit_after_lock_table");
7625
7626 44945 const bool new_clustered = ctx0->need_rebuild();
7627 44945 trx_t *trx = ctx0->trx;
7628 44945 bool fail = false;
7629
7630
2/2
✓ Branch 0 taken 28228 times.
✓ Branch 1 taken 16717 times.
44945 if (new_clustered) {
7631
2/2
✓ Branch 0 taken 30252 times.
✓ Branch 1 taken 28228 times.
58480 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7632 30252 ha_innobase_inplace_ctx *ctx =
7633 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30252 times.
30252 assert(ctx->need_rebuild());
7635
7636
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 30216 times.
30252 if (ctx->old_table->fts) {
7637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 ut_ad(!ctx->old_table->fts->add_wq);
7638
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 fts_optimize_remove_table(ctx->old_table);
7639 }
7640
7641
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 30136 times.
30252 if (ctx->new_table->fts) {
7642
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 ut_ad(!ctx->new_table->fts->add_wq);
7643
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 fts_optimize_remove_table(ctx->new_table);
7644 }
7645 }
7646 }
7647
7648
2/2
✓ Branch 0 taken 11864 times.
✓ Branch 1 taken 33081 times.
44945 if (trx == nullptr) {
7649 11864 trx = m_prebuilt->trx;
7650 11864 ctx0->trx = trx;
7651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11864 times.
11864 assert(!new_clustered);
7652 }
7653
7654 /* Generate the temporary name for old table, and acquire mdl
7655 lock on it. */
7656
1/2
✓ Branch 0 taken 44945 times.
✗ Branch 1 not taken.
44945 THD *thd = current_thd;
7657
2/2
✓ Branch 0 taken 47303 times.
✓ Branch 1 taken 44945 times.
92248 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7658 47303 ha_innobase_inplace_ctx *ctx =
7659 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7660
7661
2/2
✓ Branch 0 taken 30252 times.
✓ Branch 1 taken 17051 times.
47303 if (ctx->need_rebuild()) {
7662 60504 ctx->tmp_name = dict_mem_create_temporary_tablename(
7663
1/2
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
30252 ctx->heap, ctx->new_table->name.m_name, ctx->new_table->id);
7664
7665 30252 std::string db_str;
7666 30252 std::string tbl_str;
7667
2/4
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30252 times.
✗ Branch 3 not taken.
30252 dict_name::get_table(ctx->tmp_name, db_str, tbl_str);
7668
7669 /* Acquire mdl lock on the temporary table name. */
7670 30252 MDL_ticket *mdl_ticket = nullptr;
7671
2/4
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30252 times.
30252 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
7672 false, &mdl_ticket)) {
7673 return true;
7674 }
7675
2/4
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30252 times.
✗ Branch 3 not taken.
30252 }
7676 }
7677
7678 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
7679 or lock waits can happen in it during the data dictionary operation. */
7680
1/2
✓ Branch 0 taken 44945 times.
✗ Branch 1 not taken.
44945 row_mysql_lock_data_dictionary(trx, UT_LOCATION_HERE);
7681
7682 /* Prevent the background statistics collection from accessing
7683 the tables. */
7684 for (;;) {
7685 44945 bool retry = false;
7686
7687
2/2
✓ Branch 0 taken 47303 times.
✓ Branch 1 taken 44945 times.
92248 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7688 47303 ha_innobase_inplace_ctx *ctx =
7689 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7690
7691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47303 times.
47303 assert(new_clustered == ctx->need_rebuild());
7692
7693
5/8
✓ Branch 0 taken 30252 times.
✓ Branch 1 taken 17051 times.
✓ Branch 2 taken 30252 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30252 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 47303 times.
47303 if (new_clustered && !dict_stats_stop_bg(ctx->old_table)) {
7694 retry = true;
7695 }
7696
7697
2/4
✓ Branch 0 taken 47303 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47303 times.
47303 if (!dict_stats_stop_bg(ctx->new_table)) {
7698 retry = true;
7699 }
7700 }
7701
7702
1/2
✓ Branch 0 taken 44945 times.
✗ Branch 1 not taken.
44945 if (!retry) {
7703 44945 break;
7704 }
7705
7706 DICT_STATS_BG_YIELD(trx, UT_LOCATION_HERE);
7707 }
7708
7709 /* Apply the changes to the data dictionary tables, for all partitions.*/
7710
7711
3/4
✓ Branch 0 taken 44944 times.
✓ Branch 1 taken 47303 times.
✓ Branch 2 taken 47303 times.
✗ Branch 3 not taken.
92247 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx && !fail; pctx++) {
7712 47303 ha_innobase_inplace_ctx *ctx =
7713 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7714
7715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47303 times.
47303 assert(new_clustered == ctx->need_rebuild());
7716
7717
1/2
✓ Branch 0 taken 47303 times.
✗ Branch 1 not taken.
47303 fail = commit_get_autoinc(ha_alter_info, ctx, altered_table, table);
7718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47303 times.
47303 if (fail) {
7719 my_error(ER_TABLESPACE_DISCARDED, MYF(0), table->s->table_name.str);
7720 goto rollback_trx;
7721 }
7722
7723
2/2
✓ Branch 0 taken 30252 times.
✓ Branch 1 taken 17051 times.
47303 if (ctx->need_rebuild()) {
7724 60504 fail = commit_try_rebuild(ha_alter_info, ctx, altered_table, table, trx,
7725
1/2
✓ Branch 0 taken 30252 times.
✗ Branch 1 not taken.
30252 table_share->table_name.str);
7726
7727
2/2
✓ Branch 0 taken 30251 times.
✓ Branch 1 taken 1 times.
30252 if (!fail) {
7728
1/2
✓ Branch 0 taken 30250 times.
✗ Branch 1 not taken.
30251 log_ddl->write_drop_log(trx, ctx->old_table->id);
7729 }
7730 } else {
7731 17051 fail = commit_try_norebuild(ha_alter_info, ctx, trx,
7732
1/2
✓ Branch 0 taken 17051 times.
✗ Branch 1 not taken.
17051 table_share->table_name.str);
7733 }
7734
2/6
✓ Branch 0 taken 47302 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47302 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
47302 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7735 #ifdef UNIV_DEBUG
7736 {
7737 /* Generate a dynamic dbug text. */
7738 char buf[32];
7739
7740 47302 snprintf(buf, sizeof buf, "ib_commit_inplace_fail_%u",
7741 failure_inject_count++);
7742
7743
2/6
✓ Branch 0 taken 47302 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47302 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
47302 DBUG_EXECUTE_IF(buf,
7744 my_error(ER_INTERNAL_ERROR, MYF(0), "Injected error!");
7745 fail = true;);
7746 }
7747 #endif
7748 }
7749
7750 44944 rollback_trx:
7751
7752 /* Commit or roll back the changes to the data dictionary. */
7753
7754
4/4
✓ Branch 0 taken 44936 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 28226 times.
✓ Branch 3 taken 16710 times.
44944 if (!fail && new_clustered) {
7755
2/2
✓ Branch 0 taken 30250 times.
✓ Branch 1 taken 28226 times.
58476 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7756 30250 ha_innobase_inplace_ctx *ctx =
7757 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7758
7759
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30250 times.
30250 assert(ctx->need_rebuild());
7760
7761 /* Check for any possible problems for any
7762 file operations that will be performed in
7763 commit_cache_rebuild(). */
7764 error =
7765
1/2
✓ Branch 0 taken 30250 times.
✗ Branch 1 not taken.
30250 fil_rename_precheck(ctx->old_table, ctx->new_table, ctx->tmp_name);
7766
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 30247 times.
30250 if (error != DB_SUCCESS) {
7767 /* Out of memory or a problem will occur
7768 when renaming files. */
7769 3 fail = true;
7770 3 my_error_innodb(error, ctx->old_table->name.m_name,
7771
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 ctx->old_table->flags);
7772 }
7773
2/6
✓ Branch 0 taken 30250 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30250 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30250 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7774 }
7775
7776 /* Test what happens on crash here.
7777 The data dictionary transaction should be
7778 rolled back, restoring the old table. */
7779
5/8
✓ Branch 0 taken 28226 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 28225 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
28226 DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit",
7780 log_buffer_flush_to_disk();
7781 DBUG_SUICIDE(););
7782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28225 times.
28225 ut_ad(!trx->fts_trx);
7783
7784
6/10
✓ Branch 0 taken 28225 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 28223 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
28225 DBUG_EXECUTE_IF("innodb_alter_commit_crash_after_commit",
7785 log_make_latest_checkpoint();
7786 log_buffer_flush_to_disk(); DBUG_SUICIDE(););
7787 }
7788
7789 /* Update the in-memory structures, close some handles, release
7790 temporary files, and (unless we rolled back) update persistent
7791 statistics. */
7792
2/2
✓ Branch 0 taken 47251 times.
✓ Branch 1 taken 44877 times.
92128 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7793 47251 ha_innobase_inplace_ctx *ctx =
7794 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7795
7796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47251 times.
47251 assert(ctx->need_rebuild() == new_clustered);
7797
7798
2/2
✓ Branch 0 taken 30212 times.
✓ Branch 1 taken 17039 times.
47251 if (new_clustered) {
7799
1/2
✓ Branch 0 taken 30212 times.
✗ Branch 1 not taken.
30212 innobase_online_rebuild_log_free(ctx->old_table);
7800 }
7801
7802
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 47240 times.
47251 if (fail) {
7803
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
11 if (new_clustered) {
7804
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 dict_table_close(ctx->new_table, true, false);
7805 4 ctx->new_table = nullptr;
7806 } else {
7807 /* We failed, but did not rebuild the table.
7808 Roll back any ADD INDEX, or get rid of garbage
7809 ADD INDEX that was left over from a previous
7810 ALTER TABLE statement. */
7811
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 innobase_rollback_sec_index(ctx->new_table, table, true, trx);
7812 }
7813
2/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
11 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
7814 crash_fail_inject_count++);
7815
7816 11 continue;
7817 11 }
7818
7819
1/2
✓ Branch 0 taken 47240 times.
✗ Branch 1 not taken.
47240 innobase_copy_frm_flags_from_table_share(ctx->new_table, altered_table->s);
7820
7821
2/2
✓ Branch 0 taken 30208 times.
✓ Branch 1 taken 17032 times.
47240 if (new_clustered) {
7822 /* We will reload and refresh the
7823 in-memory foreign key constraint
7824 metadata. This is a rename operation
7825 in preparing for dropping the old
7826 table. Set the table to_be_dropped bit
7827 here, so to make sure DML foreign key
7828 constraint check does not use the
7829 stale dict_foreign_t. This is done
7830 because WL#6049 (FK MDL) has not been
7831 implemented yet. */
7832 30208 ctx->old_table->to_be_dropped = true;
7833
7834
3/8
✓ Branch 0 taken 30208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30208 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30208 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
30208 DBUG_PRINT("to_be_dropped", ("table: %s", ctx->old_table->name.m_name));
7835
7836
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 30194 times.
30208 if ((ha_alter_info->handler_flags &
7837 Alter_inplace_info::ALTER_COLUMN_NAME)) {
7838
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 innobase_rename_col_discard_foreign(ha_alter_info, table,
7839 ctx->old_table);
7840 }
7841
7842 /* Rename the tablespace files. */
7843
1/2
✓ Branch 0 taken 30160 times.
✗ Branch 1 not taken.
30208 commit_cache_rebuild(ctx);
7844
7845 /* Discard the added foreign keys, because we will
7846 load them from the data dictionary. */
7847
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 30160 times.
30167 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
7848 7 dict_foreign_t *fk = ctx->add_fk[i];
7849
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 dict_foreign_free(fk);
7850 }
7851
7852 /* There is no FK on partition table */
7853
2/2
✓ Branch 0 taken 27653 times.
✓ Branch 1 taken 2507 times.
30160 if (m_share) {
7854 27653 ctx->new_table->discard_after_ddl = true;
7855 }
7856 } else {
7857 error =
7858
1/2
✓ Branch 0 taken 17032 times.
✗ Branch 1 not taken.
17032 innobase_update_foreign_cache(ctx, m_user_thd, &new_dd_tab->table());
7859
7860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17032 times.
17032 if (error != DB_SUCCESS) {
7861 /* The data dictionary cache
7862 should be corrupted now. The
7863 best solution should be to
7864 kill and restart the server,
7865 but the *.frm file has not
7866 been replaced yet. */
7867 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
7868 ER_ALTER_INFO,
7869 "InnoDB: Could not add foreign"
7870 " key constraints.");
7871 } else {
7872
2/4
✓ Branch 0 taken 17016 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17016 times.
17032 if (!commit_cache_norebuild(ctx, trx)) {
7873 ut_a(!m_prebuilt->trx->check_foreigns);
7874 }
7875
7876
1/2
✓ Branch 0 taken 17016 times.
✗ Branch 1 not taken.
17016 innobase_rename_or_enlarge_columns_cache(ha_alter_info, table,
7877 ctx->new_table);
7878
7879
1/2
✓ Branch 0 taken 17016 times.
✗ Branch 1 not taken.
17016 rename_indexes_in_cache(ctx, ha_alter_info);
7880 }
7881 }
7882
7883
1/2
✓ Branch 0 taken 47176 times.
✗ Branch 1 not taken.
47176 dict_mem_table_free_foreign_vcol_set(ctx->new_table);
7884
1/2
✓ Branch 0 taken 47176 times.
✗ Branch 1 not taken.
47176 dict_mem_table_fill_foreign_vcol_set(ctx->new_table);
7885
7886
2/6
✓ Branch 0 taken 47176 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47176 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
47176 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7887 }
7888
7889 /* Invalidate the index translation table. In partitioned
7890 tables, there is no share. */
7891
2/2
✓ Branch 0 taken 44227 times.
✓ Branch 1 taken 650 times.
44877 if (m_share) {
7892 44227 m_share->idx_trans_tbl.index_count = 0;
7893 }
7894
7895 /* Tell the InnoDB server that there might be work for
7896 utility threads: */
7897
7898
1/2
✓ Branch 0 taken 44877 times.
✗ Branch 1 not taken.
44877 srv_active_wake_master_thread();
7899
7900
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 44866 times.
44877 if (fail) {
7901
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
22 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7902 11 ha_innobase_inplace_ctx *ctx =
7903 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 assert(ctx->need_rebuild() == new_clustered);
7905
7906
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 ut_d(dict_table_check_for_dup_indexes(ctx->old_table, CHECK_ABORTED_OK));
7907
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 ut_a(fts_check_cached_index(ctx->old_table));
7908
2/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
11 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
7909 crash_fail_inject_count++);
7910 }
7911
7912
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 row_mysql_unlock_data_dictionary(trx);
7913 11 return true;
7914 }
7915
7916
2/2
✓ Branch 0 taken 44773 times.
✓ Branch 1 taken 93 times.
44866 if (ha_alter_info->virtual_column_drop_count ||
7917
2/2
✓ Branch 0 taken 284 times.
✓ Branch 1 taken 44489 times.
44773 ha_alter_info->virtual_column_add_count) {
7918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 377 times.
377 if (ctx0->old_table->get_ref_count() > 1) {
7919 row_mysql_unlock_data_dictionary(trx);
7920 my_error(ER_TABLE_REFERENCED, MYF(0));
7921 return true;
7922 }
7923
7924
2/2
✓ Branch 0 taken 377 times.
✓ Branch 1 taken 377 times.
754 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7925 377 ha_innobase_inplace_ctx *ctx =
7926 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7927
7928 /* Drop outdated table stats. */
7929
1/2
✓ Branch 0 taken 377 times.
✗ Branch 1 not taken.
377 innobase_discard_table(m_user_thd, ctx->old_table);
7930 }
7931
7932
1/2
✓ Branch 0 taken 377 times.
✗ Branch 1 not taken.
377 row_mysql_unlock_data_dictionary(trx);
7933 377 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
7934 377 return false;
7935 }
7936
7937
2/6
✓ Branch 0 taken 44489 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44489 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
44489 DBUG_EXECUTE_IF("ib_ddl_crash_after_user_trx_commit", DBUG_SUICIDE(););
7938
7939 44489 uint64_t autoinc = 0;
7940
2/2
✓ Branch 0 taken 46739 times.
✓ Branch 1 taken 44460 times.
91199 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7941 46739 ha_innobase_inplace_ctx *ctx =
7942 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7943
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46739 times.
46739 assert(ctx->need_rebuild() == new_clustered);
7944
7945
2/2
✓ Branch 0 taken 4230 times.
✓ Branch 1 taken 42509 times.
46739 if (altered_table->found_next_number_field) {
7946
2/2
✓ Branch 0 taken 4028 times.
✓ Branch 1 taken 202 times.
4230 if (ctx->max_autoinc > autoinc) {
7947 4028 autoinc = ctx->max_autoinc;
7948 }
7949
7950 4230 dict_table_t *t = ctx->new_table;
7951 4230 Field *field = altered_table->found_next_number_field;
7952
7953
1/2
✓ Branch 0 taken 4230 times.
✗ Branch 1 not taken.
4230 dict_table_autoinc_lock(t);
7954
1/2
✓ Branch 0 taken 4230 times.
✗ Branch 1 not taken.
4230 dict_table_autoinc_initialize(t, ctx->max_autoinc);
7955 4230 t->autoinc_persisted = ctx->max_autoinc - 1;
7956
1/2
✓ Branch 0 taken 4230 times.
✗ Branch 1 not taken.
4230 dict_table_autoinc_set_col_pos(t, field->field_index());
7957
1/2
✓ Branch 0 taken 4230 times.
✗ Branch 1 not taken.
4230 dict_table_autoinc_unlock(t);
7958 }
7959
7960 46739 bool add_fts = false;
7961
7962 /* Publish the created fulltext index, if any.
7963 Note that a fulltext index can be created without
7964 creating the clustered index, if there already exists
7965 a suitable FTS_DOC_ID column. If not, one will be
7966 created, implying new_clustered */
7967
2/2
✓ Branch 0 taken 44514 times.
✓ Branch 1 taken 46739 times.
91253 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7968 44514 dict_index_t *index = ctx->add_index[i];
7969
7970
2/2
✓ Branch 0 taken 446 times.
✓ Branch 1 taken 44068 times.
44514 if (index->type & DICT_FTS) {
7971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 446 times.
446 assert(index->type == DICT_FTS);
7972 /* We reset DICT_TF2_FTS here because the bit
7973 is left unset when a drop proceeds the add. */
7974 446 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
7975
1/2
✓ Branch 0 taken 446 times.
✗ Branch 1 not taken.
446 fts_add_index(index, ctx->new_table);
7976 446 add_fts = true;
7977 }
7978 }
7979
7980
1/2
✓ Branch 0 taken 46739 times.
✗ Branch 1 not taken.
46739 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ALL_COMPLETE));
7981
7982
4/4
✓ Branch 0 taken 446 times.
✓ Branch 1 taken 46293 times.
✓ Branch 2 taken 330 times.
✓ Branch 3 taken 116 times.
46739 if (add_fts && !ctx->new_table->discard_after_ddl) {
7983
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
330 fts_optimize_add_table(ctx->new_table);
7984 }
7985
7986
1/2
✓ Branch 0 taken 46739 times.
✗ Branch 1 not taken.
46739 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ABORTED_OK));
7987
2/4
✓ Branch 0 taken 46739 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46739 times.
46739 ut_a(fts_check_cached_index(ctx->new_table));
7988
7989
2/2
✓ Branch 0 taken 30112 times.
✓ Branch 1 taken 16627 times.
46739 if (new_clustered) {
7990 /* Since the table has been rebuilt, we remove
7991 all persistent statistics corresponding to the
7992 old copy of the table (which was renamed to
7993 ctx->tmp_name). */
7994
7995 char errstr[ERROR_STR_LENGTH];
7996
7997
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30112 times.
30112 assert(0 == strcmp(ctx->old_table->name.m_name, ctx->tmp_name));
7998
7999
4/6
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30111 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
30112 DBUG_EXECUTE_IF("ib_rename_index_fail3",
8000 DBUG_SET("+d,innodb_report_deadlock"););
8001
8002
1/2
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
30112 if (dict_stats_drop_table(ctx->new_table->name.m_name, errstr,
8003
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30111 times.
30112 sizeof(errstr)) != DB_SUCCESS) {
8004 1 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
8005 ER_ALTER_INFO,
8006 "Deleting persistent statistics"
8007 " for rebuilt table '%s' in"
8008 " InnoDB failed: %s",
8009
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 table->s->table_name.str, errstr);
8010 }
8011
8012
4/6
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30111 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
30112 DBUG_EXECUTE_IF("ib_rename_index_fail3",
8013 DBUG_SET("-d,innodb_report_deadlock"););
8014
8015
2/6
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30112 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30112 DBUG_EXECUTE_IF("ib_ddl_crash_before_commit", DBUG_SUICIDE(););
8016
8017
4/6
✓ Branch 0 taken 28172 times.
✓ Branch 1 taken 1940 times.
✓ Branch 2 taken 28172 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 30112 times.
30112 ut_ad(m_prebuilt != ctx->prebuilt || ctx == ctx0);
8018 30112 bool update_own_prebuilt = (m_prebuilt == ctx->prebuilt);
8019 30112 trx_t *const user_trx = m_prebuilt->trx;
8020
3/4
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2459 times.
✓ Branch 3 taken 27653 times.
30112 if (dict_table_is_partition(ctx->new_table)) {
8021 /* Set blob_heap to NULL for partitioned tables to avoid
8022 row_prebuilt_free() from freeing them. We do this to avoid double free
8023 of blob_heap since all partitions point to the same blob_heap in
8024 prebuilt. Blob heaps of all the partitions will be freed later in the
8025 ha_innopart::clear_blob_heaps() */
8026 2459 ctx->prebuilt->blob_heap = nullptr;
8027 }
8028
8029
1/2
✓ Branch 0 taken 30112 times.
✗ Branch 1 not taken.
30112 row_prebuilt_free(ctx->prebuilt, true);
8030
8031 /* Drop the copy of the old table, which was
8032 renamed to ctx->tmp_name at the atomic DDL
8033 transaction commit. If the system crashes
8034 before this is completed, some orphan tables
8035 with ctx->tmp_name may be recovered. */
8036 30112 ddl::drop_table(trx, ctx->old_table);
8037
8038 /* Rebuild the prebuilt object. */
8039 30083 ctx->prebuilt =
8040
1/2
✓ Branch 0 taken 30083 times.
✗ Branch 1 not taken.
30083 row_create_prebuilt(ctx->new_table, altered_table->s->reclength);
8041
2/2
✓ Branch 0 taken 28151 times.
✓ Branch 1 taken 1932 times.
30083 if (update_own_prebuilt) {
8042 28151 m_prebuilt = ctx->prebuilt;
8043 }
8044 30083 user_trx->will_lock++;
8045 30083 m_prebuilt->trx = user_trx;
8046 }
8047
2/6
✓ Branch 0 taken 46710 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46710 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
46710 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8048 }
8049
8050
1/2
✓ Branch 0 taken 44460 times.
✗ Branch 1 not taken.
44460 row_mysql_unlock_data_dictionary(trx);
8051
8052
2/2
✓ Branch 0 taken 3963 times.
✓ Branch 1 taken 40497 times.
44460 if (altered_table->found_next_number_field != nullptr) {
8053
2/4
✓ Branch 0 taken 3963 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3963 times.
✗ Branch 3 not taken.
3963 dd_set_autoinc(new_dd_tab->se_private_data(), autoinc);
8054 }
8055
8056
4/6
✓ Branch 0 taken 44460 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 44458 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
44460 DBUG_EXECUTE_IF("ib_ddl_crash_before_update_stats", DBUG_SUICIDE(););
8057
8058 /* Rebuild index translation table now for temporary tables if we are
8059 restoring secondary keys, as ha_innobase::open will not be called for
8060 the next access. */
8061
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 44449 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 44454 times.
44467 if (DICT_TF2_FLAG_IS_SET(ctx0->new_table, DICT_TF2_TEMPORARY) &&
8062
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 ctx0->num_to_add_index) {
8063
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(!ctx0->num_to_drop_index);
8064
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(!ctx0->num_to_rename);
8065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(!ctx0->num_to_drop_fk);
8066
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!innobase_build_index_translation(altered_table, ctx0->new_table,
8067 m_share)) {
8068 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
8069 return true;
8070 }
8071 }
8072
8073 /* TODO: The following code could be executed
8074 while allowing concurrent access to the table
8075 (MDL downgrade). */
8076
8077
2/2
✓ Branch 0 taken 28143 times.
✓ Branch 1 taken 16315 times.
44458 if (new_clustered) {
8078
2/2
✓ Branch 0 taken 30071 times.
✓ Branch 1 taken 28143 times.
58214 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
8079 30071 ha_innobase_inplace_ctx *ctx =
8080 static_cast<ha_innobase_inplace_ctx *>(*pctx);
8081
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30071 times.
30071 assert(ctx->need_rebuild());
8082
8083
1/2
✓ Branch 0 taken 30071 times.
✗ Branch 1 not taken.
30071 alter_stats_rebuild(ctx->new_table, table->s->table_name.str, m_user_thd);
8084
2/6
✓ Branch 0 taken 30071 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30071 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30071 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8085 }
8086 } else {
8087
2/2
✓ Branch 0 taken 16623 times.
✓ Branch 1 taken 16315 times.
32938 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
8088 16623 ha_innobase_inplace_ctx *ctx =
8089 static_cast<ha_innobase_inplace_ctx *>(*pctx);
8090
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16623 times.
16623 assert(!ctx->need_rebuild());
8091
8092
1/2
✓ Branch 0 taken 16623 times.
✗ Branch 1 not taken.
16623 alter_stats_norebuild(ha_alter_info, ctx, table->s->table_name.str,
8093 m_user_thd);
8094
2/6
✓ Branch 0 taken 16623 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16623 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
16623 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8095
8096
5/6
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 16509 times.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 114 times.
✓ Branch 5 taken 16509 times.
16737 if (ctx->fts_drop_aux_vec != nullptr &&
8097 114 ctx->fts_drop_aux_vec->aux_name.size() > 0) {
8098 114 fts_drop_dd_tables(ctx->fts_drop_aux_vec,
8099
2/4
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 dict_table_is_file_per_table(ctx->old_table));
8100 }
8101 }
8102 }
8103
8104 /* We don't support compression for the system tablespace nor
8105 the temporary tablespace. Only because they are shared tablespaces.
8106 There is no other technical reason. */
8107
8108 44458 innobase_parse_hint_from_comment(m_user_thd, m_prebuilt->table,
8109
1/2
✓ Branch 0 taken 44458 times.
✗ Branch 1 not taken.
44458 altered_table->s);
8110
8111 /* TODO: Also perform DROP TABLE and DROP INDEX after
8112 the MDL downgrade. */
8113
8114 #ifdef UNIV_DEBUG
8115
1/2
✓ Branch 0 taken 44458 times.
✗ Branch 1 not taken.
44458 dict_index_t *clust_index = ctx0->prebuilt->table->first_index();
8116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44458 times.
44458 assert(!clust_index->online_log);
8117
2/4
✓ Branch 0 taken 44458 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44458 times.
44458 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
8118
8119
3/4
✓ Branch 0 taken 63088 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63088 times.
✓ Branch 3 taken 44458 times.
107546 for (dict_index_t *index = clust_index; index; index = index->next()) {
8120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63088 times.
63088 assert(!index->to_be_dropped);
8121 }
8122 #endif /* UNIV_DEBUG */
8123 44458 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
8124 44458 return false;
8125 53014 }
8126
8127 /** Helper class for in-place alter partitioned table, see handler.h */
8128 class ha_innopart_inplace_ctx : public inplace_alter_handler_ctx {
8129 /* Only used locally in this file, so have everything public for
8130 convenience. */
8131 public:
8132 /** Total number of partitions. */
8133 uint m_tot_parts;
8134 /** Array of inplace contexts for all partitions. */
8135 inplace_alter_handler_ctx **ctx_array;
8136 /** Array of prebuilt for all partitions. */
8137 row_prebuilt_t **prebuilt_array;
8138 /** Array of old table information needed for writing back to DD */
8139 alter_table_old_info_t *m_old_info;
8140
8141 1040 ha_innopart_inplace_ctx(uint tot_parts)
8142 1040 : inplace_alter_handler_ctx(),
8143 1040 m_tot_parts(tot_parts),
8144 1040 ctx_array(),
8145 1040 prebuilt_array(),
8146 1040 m_old_info() {}
8147
8148 1718 ~ha_innopart_inplace_ctx() override {
8149
1/2
✓ Branch 0 taken 859 times.
✗ Branch 1 not taken.
1718 if (ctx_array) {
8150
2/2
✓ Branch 0 taken 3862 times.
✓ Branch 1 taken 859 times.
9442 for (uint i = 0; i < m_tot_parts; i++) {
8151 7724 destroy(ctx_array[i]);
8152 }
8153 1718 ut::free(ctx_array);
8154 }
8155
8156
1/2
✓ Branch 0 taken 859 times.
✗ Branch 1 not taken.
1718 if (m_old_info != nullptr) {
8157 1718 ut::free(m_old_info);
8158 }
8159
8160
1/2
✓ Branch 0 taken 859 times.
✗ Branch 1 not taken.
1718 if (prebuilt_array) {
8161 /* First entry is the original prebuilt! */
8162
2/2
✓ Branch 0 taken 3003 times.
✓ Branch 1 taken 859 times.
7724 for (uint i = 1; i < m_tot_parts; i++) {
8163 /* Don't close the tables. */
8164 6006 prebuilt_array[i]->table = nullptr;
8165 6006 row_prebuilt_free(prebuilt_array[i], false);
8166 }
8167 1718 ut::free(prebuilt_array);
8168 }
8169 }
8170 };
8171
8172 /** Helper class for encapsulating new/altered partitions during
8173 ADD(HASH/KEY)/COALESCE/REORGANIZE PARTITION. Here as many partition slots
8174 as in new table would be created, it's OK for ADD/COALESCE PARTITION,
8175 however more partition slots would probably be created for REORGANIZE PARTITION.
8176 Considering that it's easy to get table in this way, it's still OK. */
8177 class Altered_partitions {
8178 public:
8179 /** Constructor
8180 @param[in] parts total partitions */
8181 593 Altered_partitions(uint parts)
8182 593 : m_new_table_parts(),
8183 593 m_ins_nodes(),
8184 593 m_sql_stat_start(),
8185 593 m_trx_ids(),
8186 593 m_num_new_parts(parts) {}
8187
8188 /** Destructor */
8189 ~Altered_partitions();
8190
8191 /** Initialize the object.
8192 @return false on success
8193 @retval true on failure */
8194 bool initialize();
8195
8196 /** Open and set currently used partition.
8197 @param[in] new_part_id Partition id to set.
8198 @param[in,out] part Internal table object to use. */
8199 1503 void set_part(ulint new_part_id, dict_table_t *part) {
8200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1503 times.
1503 ut_ad(m_new_table_parts[new_part_id] == nullptr);
8201 1503 m_new_table_parts[new_part_id] = part;
8202 1503 part->skip_alter_undo = true;
8203 1503 m_sql_stat_start.set(new_part_id);
8204 1503 }
8205
8206 /** Get lower level internal table object for partition.
8207 @param[in] part_id Partition id.
8208 @return Lower level internal table object for the partition id. */
8209 2687 dict_table_t *part(uint part_id) {
8210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2687 times.
2687 ut_ad(part_id < m_num_new_parts);
8211 2687 return (m_new_table_parts[part_id]);
8212 }
8213
8214 /** To write a row, set up prebuilt for using a specified partition.
8215 @param[in,out] prebuilt Prebuilt to update.
8216 @param[in] new_part_id Partition to use. */
8217 2682 void prepare_write(row_prebuilt_t *prebuilt, uint new_part_id) const {
8218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2682 times.
2682 ut_ad(m_new_table_parts[new_part_id]);
8219 2682 prebuilt->table = m_new_table_parts[new_part_id];
8220 2682 prebuilt->ins_node = m_ins_nodes[new_part_id];
8221 2682 prebuilt->trx_id = m_trx_ids[new_part_id];
8222 2682 prebuilt->sql_stat_start = m_sql_stat_start.test(new_part_id);
8223 2682 }
8224
8225 /** After a write, update cached values for a partition from prebuilt.
8226 @param[in,out] prebuilt Prebuilt to copy from.
8227 @param[in] new_part_id Partition id to copy. */
8228 2682 void finish_write(row_prebuilt_t *prebuilt, uint new_part_id) {
8229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2682 times.
2682 ut_ad(m_new_table_parts[new_part_id] == prebuilt->table);
8230 2682 m_ins_nodes[new_part_id] = prebuilt->ins_node;
8231 2682 m_trx_ids[new_part_id] = prebuilt->trx_id;
8232
1/2
✓ Branch 0 taken 2682 times.
✗ Branch 1 not taken.
2682 if (!prebuilt->sql_stat_start) {
8233 2682 m_sql_stat_start.set(new_part_id, false);
8234 }
8235 2682 }
8236
8237 private:
8238 /** New partitions created during ADD(HASH/KEY)/COALESCE/REORGANIZE
8239 PARTITION. */
8240 dict_table_t **m_new_table_parts;
8241
8242 /** Insert nodes per partition. */
8243 ins_node_t **m_ins_nodes;
8244
8245 /** bytes for sql_stat_start bitset */
8246 byte *m_bitset;
8247
8248 /** sql_stat_start per partition */
8249 Sql_stat_start_parts m_sql_stat_start;
8250
8251 /** Trx id per partition. */
8252 trx_id_t *m_trx_ids;
8253
8254 /** Number of new partitions. */
8255 size_t m_num_new_parts;
8256 };
8257
8258 /** Destructor */
8259 484 Altered_partitions::~Altered_partitions() {
8260
1/2
✓ Branch 0 taken 484 times.
✗ Branch 1 not taken.
484 if (m_new_table_parts != nullptr) {
8261
2/2
✓ Branch 0 taken 1957 times.
✓ Branch 1 taken 484 times.
2441 for (ulint i = 0; i < m_num_new_parts; i++) {
8262
2/2
✓ Branch 0 taken 1326 times.
✓ Branch 1 taken 631 times.
1957 if (m_new_table_parts[i] != nullptr) {
8263 1326 m_new_table_parts[i]->skip_alter_undo = false;
8264 }
8265 }
8266
8267 484 ut::free(m_new_table_parts);
8268 }
8269
8270
1/2
✓ Branch 0 taken 484 times.
✗ Branch 1 not taken.
484 if (m_ins_nodes != nullptr) {
8271
2/2
✓ Branch 0 taken 1957 times.
✓ Branch 1 taken 484 times.
2441 for (ulint i = 0; i < m_num_new_parts; i++) {
8272
2/2
✓ Branch 0 taken 771 times.
✓ Branch 1 taken 1186 times.
1957 if (m_ins_nodes[i] != nullptr) {
8273 771 ins_node_t *ins = m_ins_nodes[i];
8274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 771 times.
771 ut_ad(ins->select == nullptr);
8275 771 que_graph_free_recursive(ins->select);
8276 771 ins->select = nullptr;
8277
1/2
✓ Branch 0 taken 771 times.
✗ Branch 1 not taken.
771 if (ins->entry_sys_heap != nullptr) {
8278 771 mem_heap_free(ins->entry_sys_heap);
8279 771 ins->entry_sys_heap = nullptr;
8280 }
8281 }
8282 }
8283
8284 484 ut::free(m_ins_nodes);
8285 }
8286
8287 484 ut::free(m_bitset);
8288 484 ut::free(m_trx_ids);
8289 484 }
8290
8291 /** Initialize the object.
8292 @return false on success else true. */
8293 593 bool Altered_partitions::initialize() {
8294 593 size_t alloc_size = sizeof(*m_new_table_parts) * m_num_new_parts;
8295 593 m_new_table_parts = static_cast<dict_table_t **>(ut::zalloc_withkey(
8296 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8297
8298 593 alloc_size = sizeof(*m_ins_nodes) * m_num_new_parts;
8299 593 m_ins_nodes = static_cast<ins_node_t **>(ut::zalloc_withkey(
8300 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8301
8302 593 alloc_size = sizeof(*m_bitset) * UT_BITS_IN_BYTES(m_num_new_parts);
8303 593 m_bitset = static_cast<byte *>(ut::zalloc_withkey(
8304 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8305
8306 593 alloc_size = sizeof(*m_trx_ids) * m_num_new_parts;
8307 593 m_trx_ids = static_cast<trx_id_t *>(ut::zalloc_withkey(
8308 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8309
8310
2/4
✓ Branch 0 taken 593 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 593 times.
✗ Branch 3 not taken.
593 if (m_new_table_parts == nullptr || m_ins_nodes == nullptr ||
8311
2/4
✓ Branch 0 taken 593 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 593 times.
593 m_bitset == nullptr || m_trx_ids == nullptr) {
8312 ut::free(m_new_table_parts);
8313 ut::free(m_ins_nodes);
8314 ut::free(m_bitset);
8315 ut::free(m_trx_ids);
8316
8317 return (true);
8318 }
8319
8320 593 m_sql_stat_start.init(m_bitset, UT_BITS_IN_BYTES(m_num_new_parts));
8321
8322 593 return (false);
8323 }
8324
8325 /** Class(interface) which manages the operations for partitions of states
8326 in different categories during ALTER PARTITION. There are four categories
8327 for now:
8328 1. normal: mapping to PART_NORMAL, which means the partition is not changed
8329 2. add: mapping to PART_TO_BE_ADDED
8330 3. drop: mapping to PART_TO_BE_DROPPED, PART_TO_BE_REORGED
8331 and PART_REORGED_DROPPED
8332 4. change: mapping to PART_CHANGED */
8333 class alter_part {
8334 public:
8335 /** Virtual destructor */
8336 9484 virtual ~alter_part() = default;
8337
8338 /** Return the partition id */
8339 6181 virtual uint part_id() const { return (m_part_id); }
8340
8341 /** Return the partition state */
8342 9678 virtual partition_state state() const { return (m_state); }
8343
8344 /** Get the InnoDB table object for newly created partition
8345 if applicable
8346 @return the InnoDB table object or nullptr if not applicable */
8347 3665 dict_table_t *new_table() { return (m_new); }
8348
8349 /** Set the freed old partition to nullptr to avoid dangling pointer
8350 @param check_in_cache whether we need to check table in cache
8351 @param part_name Partitioned table name .*/
8352 1978 inline void free_old_part(bool check_in_cache, const char *part_name) {
8353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1978 times.
1978 if (check_in_cache) {
8354 dict_sys_mutex_enter();
8355
8356 if (!dict_table_check_if_in_cache_low(part_name)) {
8357 *m_old = nullptr;
8358 }
8359
8360 dict_sys_mutex_exit();
8361
8362 } else {
8363 1978 *m_old = nullptr;
8364 }
8365 1978 }
8366
8367 /** Prepare
8368 @param[in,out] altered_table Table definition after the ALTER
8369 @param[in] old_part the stored old partition or nullptr
8370 if no corresponding one exists
8371 @param[in,out] new_part the stored new partition or nullptr
8372 if no corresponding one exists
8373 @return 0 or error number */
8374 1384 virtual int prepare(TABLE *altered_table [[maybe_unused]],
8375 const dd::Partition *old_part [[maybe_unused]],
8376 dd::Partition *new_part [[maybe_unused]]) {
8377 1384 return (0);
8378 }
8379
8380 /** Try to commit
8381 @param[in] table Table definition before the ALTER
8382 @param[in,out] altered_table Table definition after the ALTER
8383 @param[in] old_part the stored old partition or nullptr
8384 if no corresponding one exists
8385 @param[in,out] new_part the stored new partition or nullptr
8386 if no corresponding one exists
8387 @return 0 or error number */
8388 virtual int try_commit(const TABLE *table [[maybe_unused]],
8389 TABLE *altered_table [[maybe_unused]],
8390 const dd::Partition *old_part [[maybe_unused]],
8391 dd::Partition *new_part [[maybe_unused]]) {
8392 return (0);
8393 }
8394
8395 /** Rollback */
8396 174 virtual void rollback() { return; }
8397
8398 protected:
8399 /** Constructor
8400 @param[in,out] trx InnoDB transaction, nullptr if not used
8401 @param[in] part_id Partition id in the table. This could
8402 be partition id for either old table
8403 or new table, callers should remember
8404 which one is applicable
8405 @param[in] state Partition state of the partition on
8406 which this class will do operations.
8407 If this is for one partition in new
8408 table, the partition state is the same
8409 for both the new partition and the
8410 corresponding old partition
8411 @param[in] table_name Partitioned table name, in the
8412 form of db/table, which considers
8413 the charset
8414 @param[in,out] old InnoDB table object for old partition,
8415 default is nullptr, which means there
8416 is no corresponding object */
8417 5261 alter_part(trx_t *trx, uint part_id, partition_state state,
8418 const char *table_name, dict_table_t **old)
8419 5261 : m_trx(trx),
8420 5261 m_part_id(part_id),
8421 5261 m_state(state),
8422 5261 m_table_name(table_name),
8423 5261 m_old(old),
8424 5261 m_new(nullptr) {}
8425
8426 /** Build the partition name for specified partition
8427 @param[in] dd_part dd::Partition
8428 @param[in] temp True if this is a temporary name
8429 @param[out] name Partition name buffer of length FN_REFLEN
8430 @return true if successful. */
8431 bool build_partition_name(const dd::Partition *dd_part, bool temp,
8432 char *name);
8433
8434 /** Create a new partition
8435 @param[in] part_table partition table
8436 @param[in] part_name Partition name, including db/table
8437 @param[in,out] dd_part dd::Partition
8438 @param[in] table Table format
8439 @param[in] tablespace Tablespace of this partition,
8440 if length is 0, it means no
8441 tablespace specified
8442 @param[in] file_per_table Current value of innodb_file_per_table
8443 @param[in] autoinc Next AUTOINC value to use
8444 @param[in] autoextend_size Value of AUTOEXTEND_SIZE for this tablespace
8445 @return 0 or error number */
8446 int create(const dd::Table *part_table, const char *part_name,
8447 dd::Partition *dd_part, TABLE *table, const char *tablespace,
8448 bool file_per_table, uint64_t autoinc, uint64_t autoextend_size);
8449
8450 protected:
8451 /** InnoDB transaction, nullptr if not used */
8452 trx_t *const m_trx;
8453
8454 /** Partition id in the table. This could be partition id for
8455 either old table or new table, callers should remember which one
8456 is applicable */
8457 uint m_part_id;
8458
8459 /** Partition state of the partition on which this class will
8460 do operations. If this is for one partition in new table, the
8461 partition state is the same for both the new partition and the
8462 corresponding old partition */
8463 partition_state m_state;
8464
8465 /** Partitioned table name, in form of ./db/table, which already
8466 considers the charset */
8467 const char *m_table_name;
8468
8469 /** The InnoDB table object for old partition */
8470 dict_table_t **m_old;
8471
8472 /** The InnoDB table object for newly created partition */
8473 dict_table_t *m_new;
8474 };
8475
8476 4572 bool alter_part::build_partition_name(const dd::Partition *dd_part, bool temp,
8477 char *name) {
8478
2/4
✓ Branch 0 taken 4572 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4572 times.
4572 if (!normalize_table_name(name, m_table_name)) {
8479 /* purecov: begin inspected */
8480 ut_d(ut_error);
8481 ut_o(return (false));
8482 /* purecov: end */
8483 }
8484
8485 4572 std::string partition;
8486 /* Build the partition name. */
8487
1/2
✓ Branch 0 taken 4572 times.
✗ Branch 1 not taken.
4572 dict_name::build_partition(dd_part, partition);
8488
8489 4572 std::string partition_name;
8490 /* Build the partitioned table name. */
8491
3/6
✓ Branch 0 taken 4572 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4572 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4572 times.
✗ Branch 5 not taken.
4572 dict_name::build_table("", name, partition, temp, false, partition_name);
8492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4572 times.
4572 ut_ad(partition_name.length() < FN_REFLEN);
8493
8494 /* Copy partition table name. */
8495
1/2
✓ Branch 0 taken 4572 times.
✗ Branch 1 not taken.
4572 auto name_len = partition_name.copy(name, FN_REFLEN - 1);
8496 4572 name[name_len] = '\0';
8497
8498 4572 return (true);
8499 4572 }
8500
8501 1702 int alter_part::create(const dd::Table *old_part_table, const char *part_name,
8502 dd::Partition *dd_part, TABLE *table,
8503 const char *tablespace, bool file_per_table,
8504 uint64_t autoinc, uint64_t autoextend_size) {
8505
4/6
✓ Branch 0 taken 876 times.
✓ Branch 1 taken 826 times.
✓ Branch 2 taken 876 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1702 times.
1702 ut_ad(m_state == PART_TO_BE_ADDED || m_state == PART_CHANGED);
8506
8507
1/2
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
1702 dd::Table &dd_table = dd_part->table();
8508
1/2
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
1702 dd::Properties &options = dd_table.options();
8509 uint32_t key_block_size;
8510
3/6
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1702 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1702 times.
1702 ut_ad(options.exists("key_block_size"));
8511
2/4
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1702 times.
✗ Branch 3 not taken.
1702 options.get("key_block_size", &key_block_size);
8512
8513
1/2
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
1702 dd::Properties &part_options = dd_part->options();
8514 1702 dd::String_type data_file_name;
8515
3/4
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 1676 times.
1702 if (part_options.exists(data_file_name_key))
8516
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 (void)part_options.get(data_file_name_key, &data_file_name);
8517 /* index_file_name is not allowed for now */
8518 char full_path[FN_REFLEN];
8519
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1676 times.
1702 if (!data_file_name.empty()) {
8520 /* Have to append the postfix table name, to make it work */
8521 26 const char *name = strrchr(part_name, '/');
8522
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 ut_ad(name != nullptr);
8523 26 size_t len = data_file_name.length();
8524 26 strcpy(full_path, data_file_name.c_str());
8525 26 full_path[len] = OS_PATH_SEPARATOR;
8526 26 strcpy(full_path + len + 1, name + 1);
8527 }
8528
8529
1/2
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
1702 HA_CREATE_INFO create_info;
8530
1/2
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
1702 update_create_info_from_table(&create_info, table);
8531 1702 create_info.auto_increment_value = autoinc;
8532 1702 create_info.key_block_size = key_block_size;
8533
2/2
✓ Branch 0 taken 1676 times.
✓ Branch 1 taken 26 times.
1702 create_info.data_file_name = data_file_name.empty() ? nullptr : full_path;
8534
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1666 times.
1702 create_info.tablespace = tablespace[0] == '\0' ? nullptr : tablespace;
8535 1702 create_info.m_implicit_tablespace_autoextend_size = autoextend_size;
8536
8537 /* The below check is the same as for CREATE TABLE, but since we are
8538 doing an alter here it will not trigger the check in
8539 create_option_tablespace_is_valid(). */
8540 1702 if (tablespace_is_shared_space(&create_info) &&
8541
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1702 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1702 times.
1702 create_info.data_file_name != nullptr &&
8542 create_info.data_file_name[0] != '\0') {
8543 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION,
8544 "InnoDB: DATA DIRECTORY cannot be used"
8545 " with a TABLESPACE assignment.",
8546 MYF(0));
8547 return (HA_WRONG_CREATE_OPTION);
8548 }
8549
8550 5031 return (innobase_basic_ddl::create_impl<dd::Partition>(
8551
2/4
✓ Branch 0 taken 1702 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1627 times.
✗ Branch 3 not taken.
1702 current_thd, part_name, table, &create_info, dd_part, file_per_table,
8552 1627 false, false, 0, 0, old_part_table));
8553 1627 }
8554
8555 typedef std::vector<alter_part *, ut::allocator<alter_part *>> alter_part_array;
8556
8557 /** Construct all necessary alter_part_* objects according to the given
8558 partition states in both old and new tables */
8559 class alter_part_factory {
8560 public:
8561 /** Constructor
8562 @param[in,out] trx Transaction
8563 @param[in] ha_alter_info ALTER Information
8564 @param[in,out] part_share Innopart share
8565 @param[in] old_part_info Partition info of the table before
8566 ALTER TABLE */
8567 943 alter_part_factory(trx_t *trx, const Alter_inplace_info *ha_alter_info,
8568 Ha_innopart_share *part_share,
8569 partition_info *old_part_info)
8570 943 : m_trx(trx),
8571 943 m_part_share(part_share),
8572 943 m_ha_alter_info(ha_alter_info),
8573 943 m_old_part_info(old_part_info),
8574 943 m_file_per_table(srv_file_per_table) {}
8575
8576 /** Destructor */
8577 ~alter_part_factory() = default;
8578
8579 /** Create the alter_part_* objects according to the given
8580 partition states
8581 @param[in,out] to_drop To store the alter_part_* objects
8582 for partitions to be dropped
8583 @param[in,out] all_news To store the alter_part_* objects
8584 for partitions in table after
8585 ALTER TABLE
8586 @return false On success
8587 @retval true On failure */
8588 943 bool create(alter_part_array &to_drop, alter_part_array &all_news) {
8589 943 to_drop.clear();
8590 943 all_news.clear();
8591
8592
2/2
✓ Branch 0 taken 719 times.
✓ Branch 1 taken 224 times.
943 if (!(m_ha_alter_info->handler_flags &
8593 Alter_inplace_info::REORGANIZE_PARTITION)) {
8594 719 return (create_for_non_reorg(to_drop, all_news));
8595 } else {
8596 224 return (create_for_reorg(to_drop, all_news));
8597 }
8598 }
8599
8600 private:
8601 bool create_for_reorg(alter_part_array &to_drop, alter_part_array &all_news);
8602 bool create_for_non_reorg(alter_part_array &to_drop,
8603 alter_part_array &all_news);
8604 bool create_new_checking_conflict(partition_element *new_part,
8605 uint &new_part_id,
8606 alter_part_array &all_news);
8607 bool create_old_checking_conflict(partition_element *old_part,
8608 uint &old_part_id,
8609 alter_part_array &to_drop);
8610 bool is_conflict(const partition_element *new_part,
8611 const partition_element *old_part);
8612 bool create_one(alter_part_array &array, partition_element *part,
8613 uint &part_id, uint old_part_id, partition_state state,
8614 bool conflict);
8615 alter_part *create_one_low(uint &part_id, uint old_part_id,
8616 partition_state state, const char *tablespace,
8617 bool conflict);
8618
8619 private:
8620 /** InnoDB transaction */
8621 trx_t *const m_trx;
8622
8623 /** InnoDB partition specific Handler_share */
8624 Ha_innopart_share *const m_part_share;
8625
8626 /** ALTER information */
8627 const Alter_inplace_info *const m_ha_alter_info;
8628
8629 /** Partition info of the table before ALTER TABLE */
8630 partition_info *const m_old_part_info;
8631
8632 /** Current innodb_file_per_table value */
8633 bool m_file_per_table;
8634 };
8635
8636 /** Helper class for in-place alter partitions, see handler.h */
8637 class alter_parts : public inplace_alter_handler_ctx {
8638 public:
8639 /** Constructor
8640 @param[in,out] trx InnoDB transaction
8641 @param[in,out] part_share Innopart share
8642 @param[in] ha_alter_info ALTER information
8643 @param[in] old_part_info Partition info of the table before
8644 ALTER TABLE
8645 @param[in,out] new_partitions Altered partition helper */
8646 943 alter_parts(trx_t *trx, Ha_innopart_share *part_share,
8647 const Alter_inplace_info *ha_alter_info,
8648 partition_info *old_part_info, Altered_partitions *new_partitions)
8649 1886 : m_trx(trx),
8650 943 m_part_share(part_share),
8651 943 m_ha_alter_info(ha_alter_info),
8652 943 m_new_partitions(new_partitions),
8653 943 m_factory(trx, ha_alter_info, part_share, old_part_info),
8654
1/2
✓ Branch 0 taken 943 times.
✗ Branch 1 not taken.
943 m_news(),
8655
1/2
✓ Branch 0 taken 943 times.
✗ Branch 1 not taken.
1886 m_to_drop() {}
8656
8657 /** Destructor */
8658 ~alter_parts() override;
8659
8660 /** Create the to be created partitions and update internal
8661 structures with concurrent writes blocked, while preparing
8662 ALTER TABLE.
8663 @param[in] old_dd_tab dd::Table before ALTER TABLE
8664 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8665 @param[in,out] altered_table Table definition after the ALTER
8666 @return 0 or error number, my_error() should be called by callers */
8667 int prepare(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
8668 TABLE *altered_table);
8669
8670 /** Notify the storage engine that the changes made during
8671 prepare_inplace_alter_table() and inplace_alter_table()
8672 will be rolled back for all the partitions. */
8673 void rollback();
8674
8675 /** Try to commit the changes made during prepare_inplace_alter_table()
8676 inside the storage engine. This is protected by MDL_EXCLUSIVE.
8677 @param[in] old_dd_tab dd::Table before ALTER TABLE
8678 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8679 @param[in] table Table definition before the ALTER
8680 @param[in,out] altered_table Table definition after the ALTER
8681 @return 0 or error number, my_error() should be called by callers */
8682 int try_commit(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
8683 const TABLE *table, TABLE *altered_table);
8684
8685 /** Determine if this is an ALTER TABLE ... PARTITION operation
8686 @param[in] ha_alter_info thd DDL operation
8687 @return whether it is a such kind of operation */
8688 11803 static inline bool apply_to(const Alter_inplace_info *ha_alter_info) {
8689 11803 return ((ha_alter_info->handler_flags & OPERATIONS) != 0);
8690 }
8691
8692 /** Determine if copying data between partitions is necessary
8693 @param[in] ha_alter_info thd DDL operation
8694 @return whether it is necessary to copy data */
8695 4144 static inline bool need_copy(const Alter_inplace_info *ha_alter_info) {
8696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4144 times.
4144 ut_ad(apply_to(ha_alter_info));
8697
8698 /* Basically, only DROP PARTITION, ADD PARTITION for RANGE/LIST
8699 partitions don't require copying data between partitions */
8700
2/2
✓ Branch 0 taken 1143 times.
✓ Branch 1 taken 3001 times.
4144 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_PARTITION) {
8701
2/2
✓ Branch 0 taken 389 times.
✓ Branch 1 taken 754 times.
1143 switch (ha_alter_info->modified_part_info->part_type) {
8702 389 case partition_type::RANGE:
8703 case partition_type::LIST:
8704 389 return (false);
8705 754 default:
8706 754 break;
8707 }
8708 }
8709
8710 return (
8711 3755 !(ha_alter_info->handler_flags & (Alter_inplace_info::DROP_PARTITION)));
8712 }
8713
8714 private:
8715 /** Initialize the m_news and m_to_drop array here
8716 @param[in] old_dd_tab dd::Table before ALTER TABLE
8717 @param[in] new_dd_tab dd::Table after ALTER TABLE
8718 @retval true if success
8719 @retval false on failure */
8720 bool prepare_alter_part(const dd::Table &old_dd_tab, dd::Table &new_dd_tab);
8721
8722 /** Prepare or commit for all the partitions in table after ALTER TABLE
8723 @param[in] old_dd_tab dd::Table before ALTER TABLE
8724 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8725 @param[in,out] altered_table Table definition after the ALTER
8726 @param[in] prepare true if it's in prepare phase,
8727 false if it's in commit phase
8728 @return 0 or error number */
8729 int prepare_or_commit_for_new(const dd::Table &old_dd_tab,
8730 dd::Table &new_dd_tab, TABLE *altered_table,
8731 bool prepare);
8732
8733 /** Prepare or commit for all the partitions in table before ALTER TABLE
8734 @param[in] old_dd_tab dd::Table before ALTER TABLE
8735 @param[in,out] altered_table Table definition after the ALTER
8736 @param[in] prepare true if it's in prepare phase,
8737 false if it's in commit phase
8738 @return 0 or error number */
8739 int prepare_or_commit_for_old(const dd::Table &old_dd_tab,
8740 TABLE *altered_table, bool prepare);
8741
8742 public:
8743 /** Operations that the native partitioning can perform inplace */
8744 static constexpr Alter_inplace_info::HA_ALTER_FLAGS OPERATIONS =
8745 Alter_inplace_info::ADD_PARTITION | Alter_inplace_info::DROP_PARTITION |
8746 Alter_inplace_info::ALTER_REBUILD_PARTITION |
8747 Alter_inplace_info::COALESCE_PARTITION |
8748 Alter_inplace_info::REORGANIZE_PARTITION;
8749
8750 private:
8751 /** InnoDB transaction */
8752 trx_t *const m_trx;
8753
8754 /** InnoDB partition specific Handler_share */
8755 Ha_innopart_share *const m_part_share;
8756
8757 /** Operation being performed */
8758 const Alter_inplace_info *const m_ha_alter_info;
8759
8760 /** New partitions helper */
8761 Altered_partitions *const m_new_partitions;
8762
8763 /** alter_part factory which creates all the necessary alter_part_* */
8764 alter_part_factory m_factory;
8765
8766 /** The alter_part array for all the newly created partitions */
8767 alter_part_array m_news;
8768
8769 /** The alter_part array for all the to be dropped partitions */
8770 alter_part_array m_to_drop;
8771 };
8772
8773 /** Class which handles the partition of state PART_NORMAL.
8774 See comments for alter_part_factory::create_for_reorg
8775 and alter_part_factory::create_for_non_reorg. */
8776 class alter_part_normal : public alter_part {
8777 public:
8778 /** Constructor
8779 @param[in] part_id Partition id in the table. This could
8780 be partition id for either old table
8781 or new table, callers should remember
8782 which one is applicable
8783 @param[in] state Partition state of the partition on
8784 which this class will do operations.
8785 If this is for one partition in new
8786 table, the partition state is the same
8787 for both the new partition and the
8788 corresponding old partition
8789 @param[in,out] old InnoDB table object for old partition,
8790 default is nullptr, which means there
8791 is no corresponding object */
8792 2095 alter_part_normal(uint part_id, partition_state state, dict_table_t **old)
8793 2095 : /* Table name is not used in this class, so pass a fake
8794 one */
8795 2095 alter_part(nullptr, part_id, state, (*old)->name.m_name, old) {}
8796
8797 /** Destructor */
8798 4050 ~alter_part_normal() override = default;
8799
8800 /** Prepare
8801 @param[in,out] altered_table Table definition after the ALTER
8802 @param[in] old_part the stored old partition or nullptr
8803 if no corresponding one exists
8804 @param[in,out] new_part the stored new partition or nullptr
8805 if no corresponding one exists
8806 @return 0 or error number */
8807 2095 int prepare(TABLE *altered_table [[maybe_unused]],
8808 const dd::Partition *old_part, dd::Partition *new_part) override {
8809
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2095 times.
2095 ut_ad(old_part->name() == new_part->name());
8810
8811 2095 dd_copy_private<dd::Partition>(*new_part, *old_part);
8812
8813 2095 return (0);
8814 }
8815
8816 /** Try to commit
8817 @param[in] table Table definition before the ALTER
8818 @param[in,out] altered_table Table definition after the ALTER
8819 @param[in] old_part the stored old partition or nullptr
8820 if no corresponding one exists
8821 @param[in,out] new_part the stored new partition or nullptr
8822 if no corresponding one exists
8823 @return 0 or error number */
8824 1872 int try_commit(const TABLE *table [[maybe_unused]],
8825 TABLE *altered_table [[maybe_unused]],
8826 const dd::Partition *old_part [[maybe_unused]],
8827 dd::Partition *new_part [[maybe_unused]]) override {
8828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1872 times.
1872 ut_ad(m_old != nullptr);
8829
8830 1872 btr_drop_ahi_for_table(*m_old);
8831
8832 1872 dict_sys_mutex_enter();
8833 1872 dd_table_close(*m_old, nullptr, nullptr, true);
8834 1872 dict_table_remove_from_cache(*m_old);
8835 1872 *m_old = nullptr;
8836 1872 dict_sys_mutex_exit();
8837 1872 return (0);
8838 }
8839 };
8840
8841 /** Class which handles the partition of the state PART_TO_BE_ADDED.
8842 See comments for alter_part_factory::create_for_reorg
8843 and alter_part_factory::create_for_non_reorg. */
8844 class alter_part_add : public alter_part {
8845 public:
8846 /** Constructor
8847 @param[in] part_id Partition id in the table. This could
8848 be partition id for either old table
8849 or new table, callers should remember
8850 which one is applicable
8851 @param[in] state Partition state of the partition on
8852 which this class will do operations.
8853 If this is for one partition in new
8854 table, the partition state is the same
8855 for both the new partition and the
8856 corresponding old partition
8857 @param[in] table_name Partitioned table name, in the form
8858 of db/table, which already considers
8859 the charset
8860 @param[in] tablespace Tablespace specified explicitly
8861 @param[in,out] trx InnoDB transaction
8862 @param[in] ha_alter_info ALTER information
8863 @param[in] file_per_table Current value of innodb_file_per_table
8864 @param[in] autoinc Next autoinc value to use
8865 @param[in] conflict True if there is already a partition
8866 table with the same name */
8867 888 alter_part_add(uint part_id, partition_state state, const char *table_name,
8868 const char *tablespace, trx_t *trx,
8869 const Alter_inplace_info *ha_alter_info, bool file_per_table,
8870 uint64_t autoinc, bool conflict)
8871 888 : alter_part(trx, part_id, state, table_name, nullptr),
8872 888 m_ha_alter_info(ha_alter_info),
8873 888 m_file_per_table(file_per_table),
8874 888 m_autoinc(autoinc),
8875 888 m_conflict(conflict) {
8876
3/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 838 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
888 if (tablespace == nullptr || tablespace[0] == '\0') {
8877 838 m_tablespace[0] = '\0';
8878 } else {
8879 50 strcpy(m_tablespace, tablespace);
8880 }
8881 888 }
8882
8883 /** Destructor */
8884 1482 ~alter_part_add() override = default;
8885
8886 /** Prepare
8887 @param[in,out] altered_table Table definition after the ALTER
8888 @param[in] old_part the stored old partition or nullptr
8889 if no corresponding one exists
8890 @param[in,out] new_part the stored new partition or nullptr
8891 if no corresponding one exists
8892 @return 0 or error number */
8893 834 int prepare(TABLE *altered_table, const dd::Partition *old_part,
8894 dd::Partition *new_part) override {
8895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 834 times.
834 ut_ad(old_part != nullptr);
8896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 834 times.
834 ut_ad(new_part != nullptr);
8897 char part_name[FN_REFLEN];
8898
8899
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 826 times.
834 if (is_shared_tablespace(m_tablespace)) {
8900
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION,
8901 PARTITION_IN_SHARED_TABLESPACE, MYF(0));
8902 8 return (HA_ERR_INTERNAL_ERROR);
8903 }
8904
8905
2/4
✓ Branch 0 taken 826 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 826 times.
826 if (!build_partition_name(new_part, need_rename(), part_name)) {
8906 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
8907 }
8908
8909 /* Get the autoextend_size value from the old partition
8910 and set this value to the partition being added. */
8911
1/2
✓ Branch 0 taken 826 times.
✗ Branch 1 not taken.
826 const dd::Table &part_table = old_part->table();
8912
8913 826 ulonglong autoextend_size{};
8914
8915
2/4
✓ Branch 0 taken 826 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 826 times.
✗ Branch 3 not taken.
826 dd::get_implicit_tablespace_options(current_thd, &part_table,
8916 &autoextend_size);
8917
8918 int error =
8919
3/4
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 783 times.
✓ Branch 2 taken 787 times.
✗ Branch 3 not taken.
826 create(dd_table_has_instant_cols(part_table) ? &part_table : nullptr,
8920 826 part_name, new_part, altered_table, m_tablespace,
8921
1/2
✓ Branch 0 taken 826 times.
✗ Branch 1 not taken.
826 m_file_per_table, m_autoinc, autoextend_size);
8922
8923
7/8
✓ Branch 0 taken 783 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 783 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 664 times.
✓ Branch 5 taken 119 times.
✓ Branch 6 taken 664 times.
✓ Branch 7 taken 123 times.
787 if (error == 0 && alter_parts::need_copy(m_ha_alter_info)) {
8924 /* If partition belongs to table with instant columns, copy instant
8925 metadata to new table DD */
8926
4/6
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 664 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 640 times.
664 if (dd_table_has_row_versions(old_part->table())) {
8927
3/6
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 inherit_instant_metadata(&old_part->table(), &new_part->table());
8928 }
8929
8930
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
664 dict_sys_mutex_enter();
8931
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
664 m_new = dict_table_check_if_in_cache_low(part_name);
8932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 664 times.
664 ut_ad(m_new != nullptr);
8933
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
664 m_new->acquire();
8934
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
664 dict_table_ddl_release(m_new);
8935
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
664 dict_sys_mutex_exit();
8936
8937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 664 times.
664 return (m_new == nullptr ? DB_TABLE_NOT_FOUND : 0);
8938 }
8939
8940 123 return (error);
8941 }
8942
8943 /** Try to commit
8944 @param[in] table Table definition before the ALTER
8945 @param[in,out] altered_table Table definition after the ALTER
8946 @param[in] old_part the stored old partition or nullptr
8947 if no corresponding one exists
8948 @param[in,out] new_part the stored new partition or nullptr
8949 if no corresponding one exists
8950 @return 0 or error number */
8951 713 int try_commit(const TABLE *table [[maybe_unused]],
8952 TABLE *altered_table [[maybe_unused]],
8953 const dd::Partition *old_part [[maybe_unused]],
8954 dd::Partition *new_part) override {
8955 713 int error = 0;
8956
8957
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 650 times.
713 if (need_rename()) {
8958 char old_name[FN_REFLEN];
8959 char new_name[FN_REFLEN];
8960
8961
3/6
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
126 if (build_partition_name(new_part, true, old_name) &&
8962
2/4
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
63 build_partition_name(new_part, false, new_name)) {
8963 60 error = innobase_basic_ddl::rename_impl<dd::Partition>(
8964
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
63 m_trx->mysql_thd, old_name, new_name, new_part, new_part, nullptr);
8965
8966 } else {
8967 error = HA_ERR_TOO_LONG_PATH; /* purecov: inspected */
8968 }
8969 }
8970
8971
2/2
✓ Branch 0 taken 599 times.
✓ Branch 1 taken 111 times.
710 if (m_new != nullptr) {
8972 599 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
8973 599 m_new = nullptr;
8974 }
8975
8976 710 return (error);
8977 }
8978
8979 /** Rollback */
8980 31 void rollback() override {
8981 /* Release the new table so that in post DDL, this table can be
8982 rolled back. */
8983
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 17 times.
31 if (m_new != nullptr) {
8984 14 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
8985 14 m_new = nullptr;
8986 }
8987 31 }
8988
8989 private:
8990 /** Check if the new partition file needs a temporary name and
8991 should be renamed at last */
8992 1539 bool need_rename() const { return (m_conflict); }
8993
8994 /** Inherit instant metadata of dd::Table and dd::Columns belonging to it.
8995 This is used when a new partition is added as part of REORGANIZE partition.
8996 @param[in] source Source dd table
8997 @param[in,out] dest Destination dd table */
8998 void inherit_instant_metadata(const dd::Table *source, dd::Table *dest);
8999
9000 private:
9001 /** ALTER information */
9002 const Alter_inplace_info *m_ha_alter_info;
9003
9004 /** Current value of innodb_file_per_table */
9005 const bool m_file_per_table;
9006
9007 /** Next AUTOINC value to use */
9008 const uint64_t m_autoinc;
9009
9010 /** True if there is already a partition table with the same name */
9011 const bool m_conflict;
9012
9013 /** Tablespace of this partition */
9014 char m_tablespace[FN_REFLEN + 1];
9015 };
9016
9017 24 void alter_part_add::inherit_instant_metadata(const dd::Table *source,
9018 dd::Table *dest) {
9019 60 auto add_dropped_column = [&](const dd::Column *column) {
9020 60 const char *col_name = column->name().c_str();
9021 /* Add this column as an SE_HIDDEN column in dest table def */
9022 240 dd::Column *new_column = dd_add_hidden_column(
9023 60 dest, col_name, column->char_length(), column->type());
9024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 ut_ad(new_column != nullptr);
9025
9026 /* Copy se private data */
9027
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 ut_ad(!column->se_private_data().empty());
9028 60 new_column->se_private_data().clear();
9029 60 new_column->set_se_private_data(column->se_private_data());
9030
9031 60 new_column->set_nullable(column->is_nullable());
9032 60 new_column->set_char_length(column->char_length());
9033 60 new_column->set_numeric_scale(column->numeric_scale());
9034 60 new_column->set_unsigned(column->is_unsigned());
9035 60 new_column->set_collation_id(column->collation_id());
9036 60 new_column->set_type(column->type());
9037 /* Elements for enum columns */
9038
2/4
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
120 if (column->type() == dd::enum_column_types::ENUM ||
9039
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 column->type() == dd::enum_column_types::SET) {
9040 for (const auto *source_elem : column->elements()) {
9041 auto *elem_obj = new_column->add_element();
9042 elem_obj->set_name(source_elem->name());
9043 }
9044 }
9045 60 };
9046
9047 /* Copy dd::Column instant metadata */
9048
6/10
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 204 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 204 times.
✓ Branch 9 taken 24 times.
228 for (auto src_col : source->columns()) {
9049 dd::Column *dest_col =
9050
2/4
✓ Branch 0 taken 204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 204 times.
✗ Branch 3 not taken.
204 const_cast<dd::Column *>(dd_find_column(dest, src_col->name().c_str()));
9051
9052
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 144 times.
204 if (dest_col == nullptr) {
9053
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 add_dropped_column(src_col);
9054
3/6
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 60 times.
60 ut_ad(nullptr != dd_find_column(dest, src_col->name().c_str()));
9055 60 continue;
9056 }
9057
9058
2/4
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 144 times.
144 if (dest_col->is_virtual()) {
9059 continue;
9060 }
9061
9062 936 auto fn = [&](const char *s, auto &value) {
9063
4/6
✓ Branch 0 taken 468 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 468 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 216 times.
✓ Branch 5 taken 252 times.
936 if (src_col->se_private_data().exists(s)) {
9064
2/4
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 216 times.
✗ Branch 3 not taken.
432 src_col->se_private_data().get(s, &value);
9065
2/4
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 216 times.
✗ Branch 3 not taken.
432 dest_col->se_private_data().set(s, value);
9066 }
9067 1080 };
9068
9069 144 uint32_t v_added = UINT32_UNDEFINED;
9070 144 const char *s = dd_column_key_strings[DD_INSTANT_VERSION_ADDED];
9071
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 fn(s, v_added);
9072
9073 144 uint32_t v_dropped = UINT32_UNDEFINED;
9074 144 s = dd_column_key_strings[DD_INSTANT_VERSION_DROPPED];
9075
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 fn(s, v_dropped);
9076
9077 144 uint32_t phy_pos = UINT32_UNDEFINED;
9078 144 s = dd_column_key_strings[DD_INSTANT_PHYSICAL_POS];
9079
4/8
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 144 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 144 times.
144 ut_ad(src_col->se_private_data().exists(s));
9080
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 fn(s, phy_pos);
9081
9082 144 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL];
9083
5/8
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 144 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 138 times.
144 if (src_col->se_private_data().exists(s)) {
9084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 ut_ad(v_added > 0);
9085 6 bool value = false;
9086
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 fn(s, value);
9087 } else {
9088 138 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT];
9089
5/8
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 138 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 30 times.
✓ Branch 7 taken 108 times.
138 if (src_col->se_private_data().exists(s)) {
9090
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 ut_ad(v_added > 0);
9091 30 dd::String_type value;
9092
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 fn(s, value);
9093 30 } else {
9094 /* This columns is not INSTANT ADD or this column is already dropped. */
9095
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 108 times.
108 ut_ad(v_added == UINT32_UNDEFINED || v_dropped > 0);
9096 }
9097 }
9098 }
9099 24 }
9100
9101 /** Class which handles the partition of states
9102 PART_TO_BE_DROPPED, PART_TO_BE_REORGED and PART_REORGED_DROPPED.
9103 See comments for alter_part_factory::create_for_reorg
9104 and alter_part_factory::create_for_non_reorg. */
9105 class alter_part_drop : public alter_part {
9106 public:
9107 /** Constructor
9108 @param[in] part_id Partition id in the table. This could
9109 be partition id for either old table
9110 or new table, callers should remember
9111 which one is applicable
9112 @param[in] state Partition state of the partition on
9113 which this class will do operations.
9114 If this is for one partition in new
9115 table, the partition state is the same
9116 for both the new partition and the
9117 corresponding old partition
9118 @param[in] table_name Partitioned table name, in the form
9119 of db/table, which already considers
9120 the charset
9121 @param[in,out] trx InnoDB transaction
9122 @param[in,out] old InnoDB table object for old partition,
9123 default is nullptr, which means there
9124 is no corresponding object
9125 @param[in] conflict True if there is already a partition
9126 table with the same name */
9127 1384 alter_part_drop(uint part_id, partition_state state, const char *table_name,
9128 trx_t *trx, dict_table_t **old, bool conflict)
9129 1384 : alter_part(trx, part_id, state, table_name, old),
9130 1384 m_conflict(conflict) {}
9131
9132 /** Destructor */
9133 2524 ~alter_part_drop() override = default;
9134
9135 /** Try to commit
9136 @param[in] table Table definition before the ALTER
9137 @param[in,out] altered_table Table definition after the ALTER
9138 @param[in] old_part the stored old partition or nullptr
9139 if no corresponding one exists
9140 @param[in,out] new_part the stored new partition or nullptr
9141 if no corresponding one exists
9142 @return 0 or error number */
9143 1306 int try_commit(const TABLE *table [[maybe_unused]],
9144 TABLE *altered_table [[maybe_unused]],
9145 const dd::Partition *old_part,
9146 dd::Partition *new_part) override {
9147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1306 times.
1306 ut_ad(new_part == nullptr);
9148
9149
1/2
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
1306 dict_sys_mutex_enter();
9150
1/2
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
1306 dict_table_ddl_acquire(*m_old);
9151
1/2
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
1306 dict_sys_mutex_exit();
9152
1/2
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
1306 dd_table_close(*m_old, nullptr, nullptr, false);
9153
9154 int error;
9155 char part_name[FN_REFLEN];
9156 1306 THD *thd = m_trx->mysql_thd;
9157
9158
2/4
✓ Branch 0 taken 1306 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1306 times.
1306 if (!build_partition_name(old_part, false, part_name)) {
9159 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9160 }
9161
9162
2/2
✓ Branch 0 taken 1236 times.
✓ Branch 1 taken 70 times.
1306 if (!m_conflict) {
9163
1/2
✓ Branch 0 taken 1226 times.
✗ Branch 1 not taken.
1236 error = innobase_basic_ddl::delete_impl<dd::Partition>(thd, part_name,
9164 old_part, nullptr);
9165
2/6
✓ Branch 0 taken 1226 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1226 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1226 DBUG_EXECUTE_IF("drop_part_fail", error = DB_ERROR;
9166 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)););
9167 } else {
9168 /* Have to rename it to a temporary name to prevent
9169 name conflict, because later deleting table doesn't
9170 remove the data file at once. Also notice that don't
9171 use the #tmp name, because it could be already used
9172 by the corresponding new partition. */
9173
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 mem_heap_t *heap = mem_heap_create(FN_REFLEN, UT_LOCATION_HERE);
9174
9175 140 char *temp_name = dict_mem_create_temporary_tablename(
9176
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 heap, (*m_old)->name.m_name, (*m_old)->id);
9177
9178 70 std::string db_str;
9179 70 std::string tbl_str;
9180
2/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
70 dict_name::get_table(temp_name, db_str, tbl_str);
9181
9182 /* Acquire mdl lock on the temporary table name. */
9183 70 MDL_ticket *mdl_ticket = nullptr;
9184
2/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70 times.
70 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
9185 false, &mdl_ticket)) {
9186 mem_heap_free(heap);
9187 return (HA_ERR_GENERIC);
9188 }
9189
9190
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
70 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9191 thd, part_name, temp_name, old_part, old_part, nullptr);
9192
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 if (error == 0) {
9193
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
67 error = innobase_basic_ddl::delete_impl<dd::Partition>(
9194 thd, temp_name, old_part, nullptr);
9195 }
9196
9197
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
65 mem_heap_free(heap);
9198
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
65 }
9199
9200
1/2
✓ Branch 0 taken 1291 times.
✗ Branch 1 not taken.
1291 free_old_part(error != 0, part_name);
9201
9202 1291 return (error);
9203 }
9204
9205 private:
9206 /** True if there is already a partition table with the same name */
9207 const bool m_conflict;
9208 };
9209
9210 /** Class which handles the partition of the state PART_CHANGED.
9211 See comments for alter_part_factory::create_for_reorg
9212 and alter_part_factory::create_for_non_reorg. */
9213 class alter_part_change : public alter_part {
9214 public:
9215 /** Constructor
9216 @param[in] part_id Partition id in the table. This could
9217 be partition id for either old table
9218 or new table, callers should remember
9219 which one is applicable
9220 @param[in] state Partition state of the partition on
9221 which this class will do operations.
9222 If this is for one partition in new
9223 table, the partition state is the same
9224 for both the new partition and the
9225 corresponding old partition
9226 @param[in] table_name Partitioned table name, in the form
9227 of db/table, which already considers
9228 the chraset
9229 @param[in] tablespace Tablespace specified explicitly
9230 @param[in,out] trx InnoDB transaction
9231 @param[in,out] old InnoDB table object for old partition,
9232 default is nullptr, which means there
9233 is no corresponding object
9234 @param[in] ha_alter_info ALTER information
9235 @param[in] file_per_table Current value of innodb_file_per_table
9236 @param[in] autoinc Next AUTOINC value to use */
9237 894 alter_part_change(uint part_id, partition_state state, const char *table_name,
9238 const char *tablespace, trx_t *trx, dict_table_t **old,
9239 const Alter_inplace_info *ha_alter_info,
9240 bool file_per_table, uint64_t autoinc)
9241 894 : alter_part(trx, part_id, state, table_name, old),
9242 894 m_ha_alter_info(ha_alter_info),
9243 894 m_file_per_table(file_per_table),
9244 894 m_autoinc(autoinc) {
9245
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 894 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
894 if (tablespace == nullptr || tablespace[0] == '\0') {
9246 894 m_tablespace[0] = '\0';
9247 } else {
9248 strcpy(m_tablespace, tablespace);
9249 }
9250 894 }
9251
9252 /** Destructor */
9253 1428 ~alter_part_change() override = default;
9254
9255 /** Prepare
9256 @param[in,out] altered_table Table definition after the ALTER
9257 @param[in] old_part the stored old partition or nullptr
9258 if no corresponding one exists
9259 @param[in,out] new_part the stored new partition or nullptr
9260 if no corresponding one exists
9261 @return 0 or error number */
9262 int prepare(TABLE *altered_table, const dd::Partition *old_part,
9263 dd::Partition *new_part) override;
9264
9265 /** Try to commit
9266 @param[in] table Table definition before the ALTER
9267 @param[in,out] altered_table Table definition after the ALTER
9268 @param[in] old_part the stored old partition or nullptr
9269 if no corresponding one exists
9270 @param[in,out] new_part the stored new partition or nullptr
9271 if no corresponding one exists
9272 @return 0 or error number */
9273 int try_commit(const TABLE *table, TABLE *altered_table,
9274 const dd::Partition *old_part,
9275 dd::Partition *new_part) override;
9276
9277 /** Rollback */
9278 43 void rollback() override {
9279 /* Release the new table so that in post DDL, this table can be
9280 rolled back. */
9281
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1 times.
43 if (m_new != nullptr) {
9282 42 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
9283 42 m_new = nullptr;
9284 }
9285 43 }
9286
9287 private:
9288 /** ALTER information */
9289 const Alter_inplace_info *m_ha_alter_info;
9290
9291 /** Current value of innodb_file_per_table */
9292 const bool m_file_per_table;
9293
9294 /** Next AUTOINC value to use */
9295 const uint64_t m_autoinc;
9296
9297 /** Tablespace of this partition */
9298 char m_tablespace[FN_REFLEN + 1];
9299 };
9300
9301 /** Prepare
9302 @param[in,out] altered_table Table definition after the ALTER
9303 @param[in] old_part the stored old partition or nullptr
9304 if no corresponding one exists
9305 @param[in,out] new_part the stored new partition or nullptr
9306 if no corresponding one exists
9307 @return 0 or error number */
9308 876 int alter_part_change::prepare(TABLE *altered_table,
9309 const dd::Partition *old_part,
9310 dd::Partition *new_part) {
9311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 876 times.
876 ut_ad(old_part != nullptr);
9312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 876 times.
876 ut_ad(new_part != nullptr);
9313
9314 /* In some scenario, it could be unnecessary to create partition
9315 with temporary name, for example, old one is in innodb_system while
9316 new one is innodb_file_per_table. However, this would result in
9317 same table name for two tables, which is confusing. So the temporary
9318 name is used always and final rename is necessary too */
9319 char part_name[FN_REFLEN];
9320
9321
2/4
✓ Branch 0 taken 876 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 876 times.
876 if (!build_partition_name(new_part, true, part_name)) {
9322 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9323 }
9324
9325 /* Copy the autoextend_size attribute for the partition being
9326 created. */
9327
1/2
✓ Branch 0 taken 876 times.
✗ Branch 1 not taken.
876 const dd::Table &part_table = old_part->table();
9328
9329 876 ulonglong autoextend_size{};
9330
9331
2/4
✓ Branch 0 taken 876 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 876 times.
✗ Branch 3 not taken.
876 dd::get_implicit_tablespace_options(current_thd, &part_table,
9332 &autoextend_size);
9333
9334 int error =
9335
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 849 times.
✓ Branch 2 taken 840 times.
✗ Branch 3 not taken.
876 create(dd_table_has_instant_cols(part_table) ? &part_table : nullptr,
9336 876 part_name, new_part, altered_table, m_tablespace, m_file_per_table,
9337
1/2
✓ Branch 0 taken 876 times.
✗ Branch 1 not taken.
876 m_autoinc, autoextend_size);
9338
9339
2/2
✓ Branch 0 taken 839 times.
✓ Branch 1 taken 1 times.
840 if (error == 0) {
9340
1/2
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
839 dict_sys_mutex_enter();
9341
1/2
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
839 m_new = dict_table_check_if_in_cache_low(part_name);
9342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 839 times.
839 ut_ad(m_new != nullptr);
9343
1/2
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
839 m_new->acquire();
9344
1/2
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
839 dict_table_ddl_release(m_new);
9345
1/2
✓ Branch 0 taken 839 times.
✗ Branch 1 not taken.
839 dict_sys_mutex_exit();
9346
9347 839 return (m_new == nullptr);
9348 }
9349
9350 1 return (error);
9351 }
9352
9353 /** Try to commit
9354 @param[in] table Table definition before the ALTER
9355 @param[in,out] altered_table Table definition after the ALTER
9356 @param[in] old_part the stored old partition or nullptr
9357 if no corresponding one exists
9358 @param[in,out] new_part the stored new partition or nullptr
9359 if no corresponding one exists
9360 @return 0 or error number */
9361 719 int alter_part_change::try_commit(const TABLE *table [[maybe_unused]],
9362 TABLE *altered_table [[maybe_unused]],
9363 const dd::Partition *old_part,
9364 dd::Partition *new_part) {
9365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
719 ut_ad(old_part != nullptr);
9366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
719 ut_ad(new_part != nullptr);
9367
3/6
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 719 times.
719 ut_ad(old_part->name() == new_part->name());
9368
9369 719 THD *thd = m_trx->mysql_thd;
9370
9371 1438 char *temp_old_name = dict_mem_create_temporary_tablename(
9372
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 (*m_old)->heap, (*m_old)->name.m_name, (*m_old)->id);
9373
9374
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_sys_mutex_enter();
9375
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_table_ddl_acquire(*m_old);
9376
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_sys_mutex_exit();
9377
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dd_table_close(*m_old, nullptr, nullptr, false);
9378
9379 719 std::string db_str;
9380 719 std::string tbl_str;
9381
2/4
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
719 dict_name::get_table(temp_old_name, db_str, tbl_str);
9382
9383 /* Acquire mdl lock on the temporary table name. */
9384 719 MDL_ticket *mdl_ticket = nullptr;
9385
2/4
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 719 times.
719 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
9386 false, &mdl_ticket)) {
9387 return (HA_ERR_GENERIC);
9388 }
9389
9390 char old_name[FN_REFLEN];
9391 char temp_name[FN_REFLEN];
9392
9393
3/6
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 719 times.
1438 if (!build_partition_name(new_part, false, old_name) ||
9394
2/4
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 719 times.
719 !build_partition_name(new_part, true, temp_name)) {
9395 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9396 }
9397
9398 int error;
9399
9400
1/2
✓ Branch 0 taken 707 times.
✗ Branch 1 not taken.
719 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9401 thd, old_name, temp_old_name, old_part, old_part, nullptr);
9402
1/2
✓ Branch 0 taken 707 times.
✗ Branch 1 not taken.
707 if (error == 0) {
9403
1/2
✓ Branch 0 taken 695 times.
✗ Branch 1 not taken.
707 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9404 thd, temp_name, old_name, new_part, new_part, nullptr);
9405
1/2
✓ Branch 0 taken 695 times.
✗ Branch 1 not taken.
695 if (error == 0) {
9406
1/2
✓ Branch 0 taken 687 times.
✗ Branch 1 not taken.
695 error = innobase_basic_ddl::delete_impl<dd::Partition>(thd, temp_old_name,
9407 old_part, nullptr);
9408
1/2
✓ Branch 0 taken 687 times.
✗ Branch 1 not taken.
687 free_old_part(error != 0, temp_old_name);
9409 }
9410 }
9411
9412
1/2
✓ Branch 0 taken 687 times.
✗ Branch 1 not taken.
687 if (m_new != nullptr) {
9413
1/2
✓ Branch 0 taken 687 times.
✗ Branch 1 not taken.
687 dd_table_close(m_new, thd, nullptr, false);
9414 687 m_new = nullptr;
9415 }
9416
9417 687 return (error);
9418 687 }
9419
9420 /** Create alter_part_* object(s) for subpartitions of a partition,
9421 or the partition itself
9422 @param[in,out] array Where to store the new object(s)
9423 @param[in] part partition_element to handle
9424 @param[in,out] part_id Partition id for both partition and
9425 subpartition, which would be increased
9426 by number of object(s) created
9427 @param[in] old_part_id Start partition id of the table before
9428 ALTER TABLE
9429 @param[in] state Partition state
9430 @param[in] conflict Only valid when state is
9431 PART_TO_BE_ADDED. True if the new
9432 (sub)partition has the same name with
9433 an exist one and they are of
9434 innodb_file_per_table
9435 @retval false On success
9436 @retval true On failure */
9437 4333 bool alter_part_factory::create_one(alter_part_array &array,
9438 partition_element *part, uint &part_id,
9439 uint old_part_id, partition_state state,
9440 bool conflict) {
9441
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 3717 times.
4333 if (part->subpartitions.elements > 0) {
9442 partition_element *sub_elem;
9443
1/2
✓ Branch 0 taken 616 times.
✗ Branch 1 not taken.
616 List_iterator_fast<partition_element> new_sub_it(part->subpartitions);
9444
2/2
✓ Branch 0 taken 1544 times.
✓ Branch 1 taken 616 times.
2160 while ((sub_elem = new_sub_it++) != nullptr) {
9445 3088 const char *tablespace = partition_get_tablespace(
9446
1/2
✓ Branch 0 taken 1544 times.
✗ Branch 1 not taken.
1544 m_ha_alter_info->create_info->tablespace, part, sub_elem);
9447 alter_part *alter =
9448
1/2
✓ Branch 0 taken 1544 times.
✗ Branch 1 not taken.
1544 create_one_low(part_id, old_part_id++, state, tablespace, conflict);
9449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1544 times.
1544 if (alter == nullptr) {
9450 return (true);
9451 }
9452
9453 1544 ++part_id;
9454
1/2
✓ Branch 0 taken 1544 times.
✗ Branch 1 not taken.
1544 array.push_back(alter);
9455 }
9456 } else {
9457 7434 const char *tablespace = partition_get_tablespace(
9458
1/2
✓ Branch 0 taken 3717 times.
✗ Branch 1 not taken.
3717 m_ha_alter_info->create_info->tablespace, part, nullptr);
9459 alter_part *alter =
9460
1/2
✓ Branch 0 taken 3717 times.
✗ Branch 1 not taken.
3717 create_one_low(part_id, old_part_id++, state, tablespace, conflict);
9461
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3717 times.
3717 if (alter == nullptr) {
9462 return (true);
9463 }
9464
9465 3717 ++part_id;
9466
1/2
✓ Branch 0 taken 3717 times.
✗ Branch 1 not taken.
3717 array.push_back(alter);
9467 }
9468
9469 4333 return (false);
9470 }
9471
9472 /** Create the specified alter_part_* object
9473 @param[in] part_id Partition id for current partition
9474
9475 @param[in] old_part_id Start partition id of the table before
9476 ALTER TABLE
9477 @param[in] state Partition state
9478 @param[in] tablespace Tablespace specified explicitly
9479 @param[in] conflict Only valid when state is
9480 PART_TO_BE_ADDED. True if the new
9481 (sub)partition has the same name with
9482 an exist one and they are of
9483 innodb_file_per_table
9484 @return alter_part_* object or nullptr */
9485 5261 alter_part *alter_part_factory::create_one_low(uint &part_id, uint old_part_id,
9486 partition_state state,
9487 const char *tablespace,
9488 bool conflict) {
9489 5261 alter_part *alter_part = nullptr;
9490
9491
4/5
✓ Branch 0 taken 2095 times.
✓ Branch 1 taken 888 times.
✓ Branch 2 taken 1384 times.
✓ Branch 3 taken 894 times.
✗ Branch 4 not taken.
5261 switch (state) {
9492 2095 case PART_NORMAL:
9493
1/2
✓ Branch 0 taken 2095 times.
✗ Branch 1 not taken.
2095 alter_part = ut::new_withkey<alter_part_normal>(
9494 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9495 2095 m_part_share->get_table_part_ref(old_part_id));
9496 2095 break;
9497 888 case PART_TO_BE_ADDED:
9498 888 alter_part = ut::new_withkey<alter_part_add>(
9499 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9500 888 m_part_share->get_table_share()->normalized_path.str, tablespace,
9501 888 m_trx, m_ha_alter_info, m_file_per_table,
9502 888 m_part_share->next_auto_inc_val, conflict);
9503 888 break;
9504 1384 case PART_TO_BE_DROPPED:
9505 case PART_TO_BE_REORGED:
9506 case PART_REORGED_DROPPED:
9507
1/2
✓ Branch 0 taken 1384 times.
✗ Branch 1 not taken.
1384 alter_part = ut::new_withkey<alter_part_drop>(
9508 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9509 1384 m_part_share->get_table_share()->normalized_path.str, m_trx,
9510 1384 m_part_share->get_table_part_ref(old_part_id), conflict);
9511 1384 break;
9512 894 case PART_CHANGED:
9513
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 alter_part = ut::new_withkey<alter_part_change>(
9514 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9515 894 m_part_share->get_table_share()->normalized_path.str, tablespace,
9516 894 m_trx, m_part_share->get_table_part_ref(old_part_id), m_ha_alter_info,
9517 894 m_file_per_table, m_part_share->next_auto_inc_val);
9518 894 break;
9519 default:
9520 ut_d(ut_error);
9521 }
9522
9523 5261 return (alter_part);
9524 }
9525
9526 /** Create alter_part_add object(s) along with checking if the
9527 partition (and its subpartitions) conflicts with any of the original ones
9528 This is only for REORGANIZE PARTITION
9529 @param[in] new_part The new partition to check
9530 @param[in,out] new_part_id Partition id for both partition and
9531 subpartition, which would be increased
9532 by number of subpartitions per partition here
9533 @param[in,out] all_news To store the alter_part_add objects here
9534 @retval false On success
9535 @retval true On failure */
9536 330 bool alter_part_factory::create_new_checking_conflict(
9537 partition_element *new_part, uint &new_part_id,
9538 alter_part_array &all_news) {
9539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 ut_ad((m_ha_alter_info->handler_flags &
9540 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9541
9542 330 partition_info *part_info = m_ha_alter_info->modified_part_info;
9543 /* To compare with this partition list which contains all the
9544 to be reorganized partitions */
9545
1/2
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
330 List_iterator_fast<partition_element> tmp_part_it(part_info->temp_partitions);
9546 partition_element *tmp_part_elem;
9547
9548
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 246 times.
675 while ((tmp_part_elem = tmp_part_it++) != nullptr) {
9549
3/4
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 345 times.
✓ Branch 3 taken 84 times.
429 if (!is_conflict(new_part, tmp_part_elem)) {
9550 345 continue;
9551 }
9552
9553
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 76 times.
84 if (m_ha_alter_info->modified_part_info->is_sub_partitioned()) {
9554 List_iterator_fast<partition_element> tmp_sub_it(
9555
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 tmp_part_elem->subpartitions);
9556 partition_element *tmp_sub_elem;
9557
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> new_sub_it(new_part->subpartitions);
9558 partition_element *new_sub_elem;
9559
9560
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
26 while ((new_sub_elem = new_sub_it++) != nullptr) {
9561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(new_sub_elem->partition_name != nullptr);
9562 18 tmp_sub_elem = tmp_sub_it++;
9563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(tmp_sub_elem != nullptr);
9564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(tmp_sub_elem->partition_name != nullptr);
9565
9566
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 bool conflict = is_conflict(new_sub_elem, tmp_sub_elem);
9567
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (create_one(all_news, new_sub_elem, new_part_id, 0, PART_TO_BE_ADDED,
9568 conflict)) {
9569 return (true);
9570 }
9571 }
9572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 ut_ad((tmp_sub_elem = tmp_sub_it++) == nullptr);
9573 } else {
9574
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (create_one(all_news, new_part, new_part_id, 0, PART_TO_BE_ADDED,
9575 true)) {
9576 return (true);
9577 }
9578 }
9579
9580 /* Once matched, all are done */
9581 84 return (false);
9582 }
9583
9584 return (
9585
1/2
✓ Branch 0 taken 246 times.
✗ Branch 1 not taken.
246 create_one(all_news, new_part, new_part_id, 0, PART_TO_BE_ADDED, false));
9586 }
9587
9588 /** Create alter_part_drop object(s) along with checking if the
9589 partition (and its subpartitions) conflicts with any of the to
9590 be created ones.
9591 This is only for REORGANIZE PARTITION
9592 @param[in] old_part The old partition to check
9593 @param[in,out] old_part_id Partition id for this partition or
9594 the first subpartition, which would
9595 be increased by number of subpartitions
9596 per partition here
9597 @param[in,out] to_drop To store the alter_part_drop objects
9598 @retval false On success
9599 @retval true On failure */
9600 359 bool alter_part_factory::create_old_checking_conflict(
9601 partition_element *old_part, uint &old_part_id, alter_part_array &to_drop) {
9602
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 359 times.
359 ut_ad((m_ha_alter_info->handler_flags &
9603 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9604
9605 359 partition_info *part_info = m_ha_alter_info->modified_part_info;
9606 /* To compare with this partition list which contains all the
9607 new to be added partitions */
9608
1/2
✓ Branch 0 taken 359 times.
✗ Branch 1 not taken.
359 List_iterator_fast<partition_element> part_it(part_info->partitions);
9609 partition_element *part_elem;
9610
9611
2/2
✓ Branch 0 taken 836 times.
✓ Branch 1 taken 275 times.
1111 while ((part_elem = part_it++) != nullptr) {
9612
3/4
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 752 times.
✓ Branch 3 taken 84 times.
836 if (!is_conflict(part_elem, old_part)) {
9613 752 continue;
9614 }
9615
9616
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 76 times.
84 if (m_ha_alter_info->modified_part_info->is_sub_partitioned()) {
9617
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> sub_it(part_elem->subpartitions);
9618 partition_element *sub_elem;
9619
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> old_sub_it(old_part->subpartitions);
9620 partition_element *old_sub_elem;
9621
9622
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
26 while ((old_sub_elem = old_sub_it++) != nullptr) {
9623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(old_sub_elem->partition_name != nullptr);
9624 18 sub_elem = sub_it++;
9625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(sub_elem != nullptr);
9626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(sub_elem->partition_name != nullptr);
9627
9628
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 bool conflict = is_conflict(sub_elem, old_sub_elem);
9629
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (create_one(to_drop, old_sub_elem, old_part_id, old_part_id,
9630 PART_TO_BE_REORGED, conflict)) {
9631 return (true);
9632 }
9633 }
9634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 ut_ad((sub_elem = sub_it++) == nullptr);
9635 } else {
9636
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (create_one(to_drop, old_part, old_part_id, old_part_id,
9637 PART_TO_BE_REORGED, true)) {
9638 return (true);
9639 }
9640 }
9641
9642 /* Once matched, all are done */
9643 84 return (false);
9644 }
9645
9646
1/2
✓ Branch 0 taken 275 times.
✗ Branch 1 not taken.
275 return (create_one(to_drop, old_part, old_part_id, old_part_id,
9647 275 PART_TO_BE_REORGED, false));
9648 }
9649
9650 /** Check if the two (sub)partitions conflict with each other,
9651 Which means they have same name.
9652 @param[in] new_part New partition to check
9653 @param[in] old_part Old partition to check
9654 @retval true Conflict
9655 @retval false Not conflict */
9656 1301 bool alter_part_factory::is_conflict(const partition_element *new_part,
9657 const partition_element *old_part) {
9658 1301 if (my_strcasecmp(system_charset_info, new_part->partition_name,
9659
2/2
✓ Branch 0 taken 1109 times.
✓ Branch 1 taken 192 times.
1301 old_part->partition_name) != 0) {
9660 1109 return (false);
9661 }
9662
9663 /* To prevent the conflict(same) names in table cache, not to
9664 check the innodb_file_per_table */
9665 192 return (true);
9666 }
9667
9668 /** Suppose that there is a table with 4 range partitions: p0, p1, p2, p3,
9669 and the p2 and p3 are going to be reorganized into p21, p22, p31, p33.
9670
9671 In modified_part_info->temp_partitions list, there are only p2 and p3
9672 with the state PART_TO_BE_REORGED, while in modified_part_info->partitions
9673 list, it contains
9674 {PART_NORMAL, PART_NORMAL, PART_TO_BE_ADDED, PART_TO_BE_ADDED,
9675 PART_TO_BE_ADDED, PART_TO_BE_ADDED}.
9676
9677 So finally, the to_drop array would contain
9678 {alter_part_drop, alter_part_drop}, which are for p2, p3;
9679 the all_news array would contains
9680 {alter_part_normal, alter_part_normal, alter_part_add, alter_part_add,
9681 alter_part_add, alter_part_add}.
9682
9683 Note that the scenario that reorganized and to be reorganized
9684 partition/subpartition have the same name, would be checked here too */
9685
9686 /** Create the alter_part_* objects when it's an operation like
9687 REORGANIZE PARTITION
9688 @param[in,out] to_drop To store the alter_part_* objects
9689 for partitions to be dropped
9690 @param[in,out] all_news To store the alter_part_* objects
9691 for partitions in table after ALTER TABLE
9692 @return false On success
9693 @retval true On failure */
9694 224 bool alter_part_factory::create_for_reorg(alter_part_array &to_drop,
9695 alter_part_array &all_news) {
9696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad((m_ha_alter_info->handler_flags &
9697 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad(m_ha_alter_info->modified_part_info->num_subparts ==
9699 m_old_part_info->num_subparts);
9700
9701 224 partition_info *part_info = m_ha_alter_info->modified_part_info;
9702 /* This list contains only the to be reorganized partitions,
9703 the sequence is the same as the list of m_old_part_info,
9704 and they should be consecutive ones */
9705
1/2
✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
224 List_iterator_fast<partition_element> tmp_part_it(part_info->temp_partitions);
9706 /* This list contains all the new partitions */
9707
1/2
✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
224 List_iterator_fast<partition_element> part_it(part_info->partitions);
9708 /* This list contains all the old partitions */
9709 List_iterator_fast<partition_element> old_part_it(
9710
1/2
✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
224 m_old_part_info->partitions);
9711 partition_element *part_elem;
9712 partition_element *tmp_part_elem;
9713 partition_element *old_part_elem;
9714 uint parts_per_part =
9715
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 209 times.
224 part_info->is_sub_partitioned() ? part_info->num_subparts : 1;
9716
9717 224 tmp_part_elem = tmp_part_it++;
9718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad(tmp_part_elem != nullptr);
9719 224 old_part_elem = old_part_it++;
9720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad(old_part_elem != nullptr);
9721
9722 224 uint old_part_id = 0;
9723 224 uint new_part_id = 0;
9724
9725 /* There are 3 steps here:
9726 1. Check if the old one is a to be reorganized one, if so, mark it
9727 and check next old one
9728 2. If not, check if the new one is a to be added one, if so, mark it
9729 and check next new one
9730 3. If not, the old one and the new one should point to the same
9731 partition */
9732
2/2
✓ Branch 0 taken 562 times.
✓ Branch 1 taken 224 times.
786 while ((part_elem = part_it++) != nullptr) {
9733
4/4
✓ Branch 0 taken 659 times.
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 519 times.
✓ Branch 3 taken 140 times.
921 while (old_part_elem != nullptr && tmp_part_elem != nullptr &&
9734
2/2
✓ Branch 0 taken 359 times.
✓ Branch 1 taken 160 times.
519 strcmp(tmp_part_elem->partition_name,
9735 old_part_elem->partition_name) == 0) {
9736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 359 times.
359 ut_ad(tmp_part_elem->part_state == PART_TO_BE_REORGED);
9737
9738
2/4
✓ Branch 0 taken 359 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 359 times.
359 if (create_old_checking_conflict(old_part_elem, old_part_id, to_drop)) {
9739 return (true);
9740 }
9741
9742 359 old_part_elem = old_part_it++;
9743 359 tmp_part_elem = tmp_part_it++;
9744 }
9745
9746
2/3
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 232 times.
✗ Branch 2 not taken.
562 switch (part_elem->part_state) {
9747 330 case PART_TO_BE_ADDED:
9748
9749
2/4
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 330 times.
330 if (create_new_checking_conflict(part_elem, new_part_id, all_news)) {
9750 return (true);
9751 }
9752
9753 330 break;
9754
9755 232 case PART_NORMAL:
9756
9757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 ut_ad(strcmp(part_elem->partition_name,
9758 old_part_elem->partition_name) == 0);
9759
9760
2/4
✓ Branch 0 taken 232 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 232 times.
232 if (create_one(all_news, part_elem, new_part_id, old_part_id,
9761 PART_NORMAL, false)) {
9762 return (true);
9763 }
9764
9765 232 old_part_elem = old_part_it++;
9766 232 old_part_id += parts_per_part;
9767
9768 232 break;
9769
9770 default:
9771 ut_d(ut_error);
9772 }
9773 }
9774
9775
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad(old_part_elem == nullptr);
9776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 ut_ad(tmp_part_elem == nullptr);
9777
9778 224 return (false);
9779 }
9780
9781 /** Suppose that there is a table with 4 range partitions: p0, p1, p2, p3.
9782
9783 1. ADD PARTITION p4
9784 modified_part_info->partitions list contains
9785 {PART_NORMAL, PART_NORMAL, PART_NORMAL, PART_NORMAL, PART_TO_BE_ADDED}.
9786
9787 So finally, the to_drop array would contain
9788 {}, which is empty;
9789 the all_news array would contains
9790 {alter_part_normal, alter_part_normal, alter_part_normal, alter_part_normal,
9791 alter_part_add}.
9792
9793 2. DROP PARTITION p2
9794 modified_part_info->partitions list contain
9795 {PART_NORMAL, PART_NORMAL, PART_TO_BE_DROPPED, PART_NORMAL}.
9796
9797 So finally, the to_drop array would contain
9798 {alter_part_drop}, which is for p2, so part_id is 2;
9799 the all_news array would contains
9800 {alter_part_normal, alter_part_normal, alter_part_normal}.
9801
9802
9803 Suppose it's the same table with 4 partitions, but it's partitioned by HASH.
9804
9805 3. ADD PARTITION 2
9806 modified_part_info->partitions list contains
9807 {PART_CHANGED, PART_CHANGED, PART_CHANGED, PART_CHANGED, PART_TO_BE_ADDED,
9808 PART_TO_BE_ADDED}.
9809
9810 So finally, the to_drop array would contain
9811 {}, which is empty;
9812 the all_news array would contains
9813 {alter_part_change, alter_part_change, alter_part_change, alter_part_change,
9814 alter_part_add, alter_part_add}.
9815
9816 4. COALESCE PARTITION 2
9817 modified_part_info->partitions contains:
9818 {PART_CHANGED, PART_CHANGED, PART_REORGED_DROPPED, PART_REORGED_DROPPED}.
9819
9820 So finally, the to_drop array would contain
9821 {alter_part_drop, alter_part_drop}, which are for p2, p3, part_id are 2 and 3;
9822 the all_news array would contains
9823 {alter_part_change, alter_part_change}.
9824
9825 5. REBUILD PARTITION p0, p2
9826 modified_part_info->partitions contains:
9827 {PART_NORMAL, PART_CHANGED, PART_NORMAL, PART_CHANGED}.
9828
9829 So finally, the to_drop array would contain
9830 {}, which is empty;
9831 the all_news array would contains
9832 {alter_part_normal, alter_part_change, alter_part_normal, alter_part_change}. */
9833
9834 /** Create the alter_part_* objects when it's NOT an operation like
9835 REORGANIZE PARTITION
9836 @param[in,out] to_drop To store the alter_part_* objects
9837 for partitions to be dropped
9838 @param[in,out] all_news To store the alter_part_* objects
9839 for partitions in table after ALTER TABLE
9840 @return false On success
9841 @retval true On Failure */
9842 719 bool alter_part_factory::create_for_non_reorg(alter_part_array &to_drop,
9843 alter_part_array &all_news) {
9844
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
719 ut_ad((m_ha_alter_info->handler_flags &
9845 Alter_inplace_info::REORGANIZE_PARTITION) == 0);
9846
9847 719 partition_info *part_info = m_ha_alter_info->modified_part_info;
9848 uint parts_per_part =
9849
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 558 times.
719 part_info->is_sub_partitioned() ? part_info->num_subparts : 1;
9850
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 List_iterator_fast<partition_element> part_it(part_info->partitions);
9851 partition_element *part_elem;
9852 719 uint old_part_id = 0;
9853 719 uint new_part_id = 0;
9854
9855
2/2
✓ Branch 0 taken 3392 times.
✓ Branch 1 taken 719 times.
4111 while ((part_elem = part_it++) != nullptr) {
9856 3392 partition_state state = part_elem->part_state;
9857
3/4
✓ Branch 0 taken 2014 times.
✓ Branch 1 taken 513 times.
✓ Branch 2 taken 865 times.
✗ Branch 3 not taken.
3392 switch (state) {
9858 2014 case PART_NORMAL:
9859 case PART_CHANGED:
9860
2/4
✓ Branch 0 taken 2014 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2014 times.
2014 if (create_one(all_news, part_elem, new_part_id, old_part_id, state,
9861 false)) {
9862 return (true);
9863 }
9864
9865 2014 old_part_id += parts_per_part;
9866 2014 break;
9867 513 case PART_TO_BE_ADDED:
9868
2/4
✓ Branch 0 taken 513 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 513 times.
513 if (create_one(all_news, part_elem, new_part_id, 0, state, false)) {
9869 return (true);
9870 }
9871
9872 513 break;
9873 865 case PART_TO_BE_DROPPED:
9874 case PART_REORGED_DROPPED:
9875
2/4
✓ Branch 0 taken 865 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 865 times.
865 if (create_one(to_drop, part_elem, old_part_id, old_part_id, state,
9876 false)) {
9877 return (true);
9878 }
9879
9880 865 break;
9881 default:
9882 ut_d(ut_error);
9883 }
9884 }
9885
9886 719 return (false);
9887 }
9888
9889 #ifndef NDEBUG
9890 /** Check if the specified partition_state is of drop state
9891 @param[in] s The state to be checked
9892 @retval true if this is of a drop state
9893 @retval false if not */
9894 810 inline static bool is_drop_state(partition_state s) {
9895
4/6
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 690 times.
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 120 times.
✗ Branch 5 not taken.
810 return (s == PART_TO_BE_DROPPED || s == PART_REORGED_DROPPED ||
9896 810 s == PART_TO_BE_REORGED);
9897 }
9898 #endif
9899
9900 /** Check if the specified partition_state is of common state
9901 @param[in] s The state to be checked
9902 @retval true if this is of a common state
9903 @retval false if not */
9904 8868 inline static bool is_common_state(partition_state s) {
9905
4/4
✓ Branch 0 taken 4901 times.
✓ Branch 1 taken 3967 times.
✓ Branch 2 taken 1595 times.
✓ Branch 3 taken 3306 times.
8868 return (s == PART_NORMAL || s == PART_CHANGED);
9906 }
9907
9908 /** Destructor */
9909 1636 alter_parts::~alter_parts() {
9910
2/2
✓ Branch 0 taken 3480 times.
✓ Branch 1 taken 818 times.
8596 for (alter_part *alter_part : m_news) {
9911 6960 ut::delete_(alter_part);
9912 }
9913
9914
2/2
✓ Branch 0 taken 1262 times.
✓ Branch 1 taken 818 times.
4160 for (alter_part *alter_part : m_to_drop) {
9915 2524 ut::delete_(alter_part);
9916 }
9917 }
9918
9919 /** Create the to be created partitions and update internal
9920 structures with concurrent writes blocked, while preparing
9921 ALTER TABLE.
9922 @param[in] old_dd_tab dd::Table before ALTER TABLE
9923 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9924 @param[in,out] altered_table Table definition after the ALTER
9925 @return 0 or error number, my_error() should be called by callers */
9926 943 int alter_parts::prepare(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
9927 TABLE *altered_table) {
9928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 943 times.
943 if (m_factory.create(m_to_drop, m_news)) {
9929 return (true);
9930 }
9931
9932
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 704 times.
943 if (m_part_share->get_table_share()->found_next_number_field) {
9933 239 dd_set_autoinc(new_dd_tab.se_private_data(),
9934 239 m_ha_alter_info->create_info->auto_increment_value);
9935 }
9936
9937 int error;
9938 943 error = prepare_or_commit_for_old(old_dd_tab, altered_table, true);
9939
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 943 times.
943 if (error != 0) {
9940 return (error);
9941 }
9942
9943 error =
9944 943 prepare_or_commit_for_new(old_dd_tab, new_dd_tab, altered_table, true);
9945
9946 /* We don't have to prepare for the partitions that will be dropped. */
9947
9948 868 return (error);
9949 }
9950
9951 /** Notify the storage engine that the changes made during
9952 prepare_inplace_alter_table() and inplace_alter_table()
9953 will be rolled back for all the partitions. */
9954 42 void alter_parts::rollback() {
9955
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 42 times.
57 for (alter_part *alter_part : m_to_drop) {
9956
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 alter_part->rollback();
9957 }
9958
9959
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 42 times.
275 for (alter_part *alter_part : m_news) {
9960
1/2
✓ Branch 0 taken 233 times.
✗ Branch 1 not taken.
233 alter_part->rollback();
9961 }
9962 42 }
9963
9964 /** Try to commit the changes made during prepare_inplace_alter_table()
9965 inside the storage engine. This is protected by MDL_EXCLUSIVE.
9966 @param[in] old_dd_tab dd::Table before ALTER TABLE
9967 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9968 @param[in] table Table definition before the ALTER
9969 @param[in,out] altered_table Table definition after the ALTER
9970 @return 0 or error number, my_error() should be called by callers */
9971 826 int alter_parts::try_commit(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
9972 const TABLE *table [[maybe_unused]],
9973 TABLE *altered_table) {
9974 int error;
9975 /* Commit for the old ones first, to clear data files for new ones */
9976 826 error = prepare_or_commit_for_old(old_dd_tab, altered_table, false);
9977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 811 times.
811 if (error != 0) {
9978 return (error);
9979 }
9980
9981 error =
9982 811 prepare_or_commit_for_new(old_dd_tab, new_dd_tab, altered_table, false);
9983
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 776 times.
776 if (error != 0) {
9984 return (error);
9985 }
9986
9987 776 return (0);
9988 }
9989
9990 /** Prepare for all the partitions in table after ALTER TABLE
9991 @param[in] old_dd_tab dd::Table before ALTER TABLE
9992 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9993 @param[in,out] altered_table Table definition after the ALTER
9994 @param[in] prepare true if it's in prepare phase,
9995 false if it's in commit phase
9996 @return 0 or error number */
9997 1754 int alter_parts::prepare_or_commit_for_new(const dd::Table &old_dd_tab,
9998 dd::Table &new_dd_tab,
9999 TABLE *altered_table, bool prepare) {
10000
1/2
✓ Branch 0 taken 1754 times.
✗ Branch 1 not taken.
1754 auto oldp = old_dd_tab.leaf_partitions().begin();
10001 1754 uint new_part_id = 0;
10002 1754 uint old_part_id = 0;
10003 1754 uint drop_seq = 0;
10004 1754 const dd::Partition *old_part = nullptr;
10005 1754 int error = 0;
10006
10007
3/4
✓ Branch 0 taken 1754 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7109 times.
✓ Branch 3 taken 1631 times.
8740 for (auto new_part : *new_dd_tab.leaf_partitions()) {
10008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7109 times.
7109 ut_ad(new_part_id < m_news.size());
10009
10010 /* To add a new partition, there is no corresponding old one,
10011 otherwise, find the old one */
10012
1/2
✓ Branch 0 taken 7109 times.
✗ Branch 1 not taken.
7109 partition_state s = m_news[new_part_id]->state();
10013
2/2
✓ Branch 0 taken 5562 times.
✓ Branch 1 taken 1547 times.
7109 if (is_common_state(s)) {
10014 5562 bool found = false;
10015
7/8
✓ Branch 0 taken 11934 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10931 times.
✓ Branch 3 taken 1003 times.
✓ Branch 4 taken 6372 times.
✓ Branch 5 taken 4559 times.
✓ Branch 6 taken 6372 times.
✓ Branch 7 taken 5562 times.
11934 for (; oldp != old_dd_tab.leaf_partitions().end() && !found; ++oldp) {
10016 6372 old_part = *oldp;
10017
10018 6372 ++old_part_id;
10019
4/4
✓ Branch 0 taken 2128 times.
✓ Branch 1 taken 4244 times.
✓ Branch 2 taken 810 times.
✓ Branch 3 taken 5562 times.
8500 if (drop_seq < m_to_drop.size() &&
10020
3/4
✓ Branch 0 taken 2128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 810 times.
✓ Branch 3 taken 1318 times.
2128 (old_part_id - 1 == m_to_drop[drop_seq]->part_id())) {
10021
2/4
✓ Branch 0 taken 810 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 810 times.
810 ut_ad(is_drop_state(m_to_drop[drop_seq]->state()));
10022 810 ++drop_seq;
10023 810 continue;
10024 }
10025
10026 5562 found = true;
10027 }
10028
10029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5562 times.
5562 ut_ad(found);
10030
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5562 times.
5562 ut_ad(drop_seq <= m_to_drop.size());
10031
3/6
✓ Branch 0 taken 5562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5562 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5562 times.
5562 ut_ad(new_part->name() == old_part->name());
10032
3/6
✓ Branch 0 taken 5562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5562 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5562 times.
5562 ut_ad((new_part->parent() == nullptr) == (old_part->parent() == nullptr));
10033
9/16
✓ Branch 0 taken 5562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2294 times.
✓ Branch 3 taken 3268 times.
✓ Branch 4 taken 2294 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2294 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2294 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2294 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2294 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 5562 times.
5562 ut_ad(new_part->parent() == nullptr ||
10034 new_part->parent()->name() == old_part->parent()->name());
10035 } else {
10036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1547 times.
1547 ut_ad(s == PART_TO_BE_ADDED);
10037 /* Let's still set one to get the old table name */
10038
1/2
✓ Branch 0 taken 1547 times.
✗ Branch 1 not taken.
1547 old_part = *(old_dd_tab.leaf_partitions().begin());
10039 }
10040
10041 7109 alter_part *alter_part = m_news[new_part_id];
10042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7109 times.
7109 ut_ad(alter_part != nullptr);
10043
10044
2/2
✓ Branch 0 taken 3805 times.
✓ Branch 1 taken 3304 times.
7109 if (prepare) {
10045
1/2
✓ Branch 0 taken 3730 times.
✗ Branch 1 not taken.
3805 error = alter_part->prepare(altered_table, old_part, new_part);
10046
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3717 times.
3730 if (error != 0) {
10047 13 return (error);
10048 }
10049
10050
6/6
✓ Branch 0 taken 2162 times.
✓ Branch 1 taken 1555 times.
✓ Branch 2 taken 1503 times.
✓ Branch 3 taken 659 times.
✓ Branch 4 taken 1503 times.
✓ Branch 5 taken 2214 times.
3717 if (m_new_partitions != nullptr && alter_part->new_table() != nullptr) {
10051
1/2
✓ Branch 0 taken 1503 times.
✗ Branch 1 not taken.
1503 m_new_partitions->set_part(new_part_id, alter_part->new_table());
10052 }
10053 } else {
10054 error =
10055
1/2
✓ Branch 0 taken 3269 times.
✗ Branch 1 not taken.
3304 alter_part->try_commit(nullptr, altered_table, old_part, new_part);
10056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3269 times.
3269 if (error != 0) {
10057 return (error);
10058 }
10059 }
10060
10061 6986 ++new_part_id;
10062 }
10063
10064 #ifdef UNIV_DEBUG
10065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1631 times.
1631 ut_ad(drop_seq <= m_to_drop.size());
10066
2/2
✓ Branch 0 taken 1759 times.
✓ Branch 1 taken 1631 times.
3390 for (uint i = drop_seq; i < m_to_drop.size(); ++i) {
10067
2/4
✓ Branch 0 taken 1759 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1759 times.
1759 ut_ad(!is_common_state(m_to_drop[i]->state()));
10068 }
10069 #endif /* UNIV_DEBUG */
10070
10071 1631 return (error);
10072 }
10073
10074 /** Prepare or commit for all the partitions in table before ALTER TABLE
10075 @param[in] old_dd_tab dd::Table before ALTER TABLE
10076 @param[in,out] altered_table Table definition after the ALTER
10077 @param[in] prepare true if it's in prepare phase,
10078 false if it's in commit phase
10079 @return 0 or error number */
10080 1769 int alter_parts::prepare_or_commit_for_old(const dd::Table &old_dd_tab,
10081 TABLE *altered_table, bool prepare) {
10082 1769 uint old_part_id = 0;
10083
1/2
✓ Branch 0 taken 1769 times.
✗ Branch 1 not taken.
1769 auto dd_part = old_dd_tab.leaf_partitions().begin();
10084 1769 int error = 0;
10085
10086
2/2
✓ Branch 0 taken 2690 times.
✓ Branch 1 taken 1754 times.
4444 for (alter_part *alter_part : m_to_drop) {
10087 2690 const dd::Partition *old_part = nullptr;
10088
10089
2/4
✓ Branch 0 taken 4053 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4053 times.
✗ Branch 3 not taken.
4053 for (; dd_part != old_dd_tab.leaf_partitions().end(); ++dd_part) {
10090
3/4
✓ Branch 0 taken 4053 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1363 times.
✓ Branch 3 taken 2690 times.
4053 if (old_part_id++ < alter_part->part_id()) {
10091 1363 continue;
10092 }
10093
10094 2690 old_part = *dd_part;
10095 2690 ++dd_part;
10096 2690 break;
10097 }
10098
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2690 times.
2690 ut_ad(old_part != nullptr);
10099
10100
2/2
✓ Branch 0 taken 1384 times.
✓ Branch 1 taken 1306 times.
2690 if (prepare) {
10101
1/2
✓ Branch 0 taken 1384 times.
✗ Branch 1 not taken.
1384 error = alter_part->prepare(altered_table, old_part, nullptr);
10102 } else {
10103
1/2
✓ Branch 0 taken 1291 times.
✗ Branch 1 not taken.
1306 error = alter_part->try_commit(nullptr, altered_table, old_part, nullptr);
10104 }
10105
10106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2675 times.
2675 if (error != 0) {
10107 return (error);
10108 }
10109 }
10110
10111 1754 return (error);
10112 }
10113
10114 /** Determine if one ALTER TABLE can be done instantly on the partitioned table
10115 @param[in] ha_alter_info the DDL operation
10116 @param[in] num_parts number of partitions
10117 @param[in] part_share the partitioned tables
10118 @param[in] old_table old TABLE
10119 @param[in] altered_table new TABLE
10120 @return Instant_Type accordingly */
10121 1085 static inline Instant_Type innopart_support_instant(
10122 const Alter_inplace_info *ha_alter_info, uint16_t num_parts,
10123 const Ha_innopart_share *part_share, const TABLE *old_table,
10124 const TABLE *altered_table) {
10125 1085 Instant_Type type = Instant_Type::INSTANT_IMPOSSIBLE;
10126
10127
2/2
✓ Branch 0 taken 1941 times.
✓ Branch 1 taken 258 times.
2199 for (uint32_t i = 0; i < num_parts; ++i) {
10128 3882 type = innobase_support_instant(
10129 1941 ha_alter_info, part_share->get_table_part(i), old_table, altered_table);
10130
2/2
✓ Branch 0 taken 827 times.
✓ Branch 1 taken 1114 times.
1941 if (type == Instant_Type::INSTANT_IMPOSSIBLE) {
10131 827 return (type);
10132 }
10133 }
10134
10135 258 return (type);
10136 }
10137
10138 int ha_innopart::parallel_scan_init(void *&scan_ctx, size_t *num_threads,
10139 bool use_reserved_threads) {
10140 auto max_threads = thd_parallel_read_threads(m_prebuilt->trx->mysql_thd);
10141 ut_a(max_threads <= Parallel_reader::MAX_THREADS);
10142
10143 max_threads = static_cast<ulong>(
10144 Parallel_reader::available_threads(max_threads, use_reserved_threads));
10145
10146 if (max_threads == 0) {
10147 return (HA_ERR_GENERIC);
10148 }
10149
10150 scan_ctx = nullptr;
10151
10152 const auto row_len = m_prebuilt->mysql_row_len;
10153
10154 auto adapter = ut::new_withkey<Parallel_reader_adapter>(
10155 UT_NEW_THIS_FILE_PSI_KEY, max_threads, row_len);
10156
10157 if (adapter == nullptr) {
10158 Parallel_reader::release_threads(max_threads);
10159 return (HA_ERR_OUT_OF_MEM);
10160 }
10161
10162 auto trx = m_prebuilt->trx;
10163
10164 innobase_register_trx(ht, ha_thd(), trx);
10165
10166 trx_start_if_not_started_xa(trx, false, UT_LOCATION_HERE);
10167
10168 trx_assign_read_view(trx);
10169
10170 const Parallel_reader::Scan_range FULL_SCAN{};
10171 const auto first_used_partition = m_part_info->get_first_used_partition();
10172
10173 for (auto i = first_used_partition; i < m_tot_parts;
10174 i = m_part_info->get_next_used_partition(i)) {
10175 set_partition(i);
10176
10177 if (dict_table_is_discarded(m_prebuilt->table)) {
10178 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
10179 m_prebuilt->table->name.m_name);
10180
10181 ut::delete_(adapter);
10182 return HA_ERR_NO_SUCH_TABLE;
10183 }
10184
10185 build_template(true);
10186
10187 Parallel_reader::Config config(FULL_SCAN, m_prebuilt->table->first_index(),
10188 0, i);
10189
10190 dberr_t err =
10191 adapter->add_scan(trx, config, [=](const Parallel_reader::Ctx *ctx) {
10192 return (adapter->process_rows(ctx));
10193 });
10194
10195 if (err != DB_SUCCESS) {
10196 ut::delete_(adapter);
10197 return (convert_error_code_to_mysql(err, 0, ha_thd()));
10198 }
10199 }
10200
10201 scan_ctx = adapter;
10202 *num_threads = max_threads;
10203
10204 adapter->set(m_prebuilt);
10205
10206 return (0);
10207 }
10208
10209 int ha_innopart::parallel_scan(void *scan_ctx, void **thread_ctxs,
10210 Parallel_reader_adapter::Init_fn init_fn,
10211 Parallel_reader_adapter::Load_fn load_fn,
10212 Parallel_reader_adapter::End_fn end_fn) {
10213 auto adapter = static_cast<Parallel_reader_adapter *>(scan_ctx);
10214
10215 auto err = adapter->run(thread_ctxs, init_fn, load_fn, end_fn);
10216
10217 return (convert_error_code_to_mysql(err, 0, ha_thd()));
10218 }
10219
10220 void ha_innopart::parallel_scan_end(void *parallel_scan_ctx) {
10221 auto adapter = static_cast<Parallel_reader_adapter *>(parallel_scan_ctx);
10222 ut::delete_(adapter);
10223 }
10224
10225 /** Check if InnoDB supports a particular alter table in-place.
10226 @param[in] altered_table TABLE object for new version of table.
10227 @param[in,out] ha_alter_info Structure describing changes to be done
10228 by ALTER TABLE and holding data used during in-place alter.
10229 @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported
10230 @retval HA_ALTER_INPLACE_NO_LOCK Supported
10231 @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE Supported, but
10232 requires lock during main phase and exclusive lock during prepare
10233 phase.
10234 @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE Supported, prepare
10235 phase requires exclusive lock. */
10236 2064 enum_alter_inplace_result ha_innopart::check_if_supported_inplace_alter(
10237 TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
10238
1/2
✓ Branch 0 taken 2064 times.
✗ Branch 1 not taken.
2064 DBUG_TRACE;
10239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2064 times.
2064 assert(ha_alter_info->handler_ctx == nullptr);
10240
10241 /* Not supporting these for partitioned tables yet! */
10242
10243 /*
10244 FK not yet supported. SQL-layer blocks most of such changes.
10245 We resort to COPY algorithm for a few which are still allowed
10246 (e.g. REMOVE PARTITIONING and ADD FOREIGN KEY at the same time).
10247 */
10248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2064 times.
2064 if (ha_alter_info->handler_flags & (Alter_inplace_info::ADD_FOREIGN_KEY |
10249 Alter_inplace_info::DROP_FOREIGN_KEY)) {
10250 ha_alter_info->unsupported_reason =
10251 innobase_get_err_msg(ER_FOREIGN_KEY_ON_PARTITIONED);
10252 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10253 }
10254 /* FTS not yet supported either. */
10255
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 1764 times.
2064 if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX)) {
10256
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 300 times.
600 for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
10257 300 const KEY *key =
10258 300 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
10259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 300 times.
300 if (key->flags & HA_FULLTEXT) {
10260 assert(!(key->flags & HA_KEYFLAG_MASK &
10261 ~(HA_FULLTEXT | HA_PACK_KEY | HA_GENERATED_KEY |
10262 HA_BINARY_PACK_KEY)));
10263 ha_alter_info->unsupported_reason =
10264 innobase_get_err_msg(ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING);
10265 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10266 }
10267 }
10268 }
10269 /* We cannot allow INPLACE to change order of KEY partitioning fields! */
10270 4128 if ((ha_alter_info->handler_flags &
10271
4/4
✓ Branch 0 taken 231 times.
✓ Branch 1 taken 1833 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 2057 times.
2295 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER) &&
10272
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 224 times.
231 !m_part_info->same_key_column_order(
10273
1/2
✓ Branch 0 taken 231 times.
✗ Branch 1 not taken.
231 &ha_alter_info->alter_info->create_list)) {
10274 7 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10275 }
10276
10277 /* Cannot allow INPLACE for drop and create PRIMARY KEY if partition is
10278 on Primary Key - PARTITION BY KEY() */
10279
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2046 times.
2057 if ((ha_alter_info->handler_flags & (Alter_inplace_info::ADD_PK_INDEX |
10280 Alter_inplace_info::DROP_PK_INDEX))) {
10281 /* Check partition by key(). */
10282 33 if ((m_part_info->part_type == partition_type::HASH) &&
10283
7/8
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 9 times.
20 m_part_info->list_of_part_fields &&
10284 9 m_part_info->part_field_list.is_empty()) {
10285 2 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10286 }
10287
10288 /* Check sub-partition by key(). */
10289 18 if ((m_part_info->subpart_type == partition_type::HASH) &&
10290
2/8
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 9 times.
9 m_part_info->list_of_subpart_fields &&
10291 m_part_info->subpart_field_list.is_empty()) {
10292 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10293 }
10294 }
10295
10296 /* Check for ALTER TABLE ... PARTITION, following operations can
10297 be done inplace */
10298
2/2
✓ Branch 0 taken 970 times.
✓ Branch 1 taken 1085 times.
2055 if (alter_parts::apply_to(ha_alter_info)) {
10299 /* Two meanings here:
10300 1. ALTER TABLE .. PARTITION could not be combined with
10301 other ALTER TABLE operations;
10302 2. Only one operation of ALTER TABLE .. PARTITION can be
10303 done in single statement. Only exception is that
10304 'ALTER TABLE table REORGANIZE PARTITION' for HASH/KEY
10305 partitions. This will flag both COALESCE_PARTITION
10306 and ALTER_TABLE_REORG;
10307 The ALTER_ALL_PARTITION should be screened out, which could only
10308 be set along with the REBUILD PARTITION */
10309
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 970 times.
970 ut_ad(is_single_bit(ha_alter_info->handler_flags &
10310 ~Alter_inplace_info::ALTER_ALL_PARTITION) ||
10311 ha_alter_info->handler_flags ==
10312 (Alter_inplace_info::COALESCE_PARTITION |
10313 Alter_inplace_info::ALTER_TABLE_REORG));
10314
4/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 964 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 970 times.
970 ut_ad(!(ha_alter_info->handler_flags &
10315 Alter_inplace_info::ALTER_ALL_PARTITION) ||
10316 (ha_alter_info->handler_flags &
10317 Alter_inplace_info::ALTER_REBUILD_PARTITION));
10318
10319
3/4
✓ Branch 0 taken 970 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 613 times.
✓ Branch 3 taken 357 times.
970 if (alter_parts::need_copy(ha_alter_info)) {
10320 613 return HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE;
10321 } else {
10322 357 return HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE;
10323 }
10324 }
10325
10326 2170 Instant_Type instant_type = innopart_support_instant(
10327
1/2
✓ Branch 0 taken 1085 times.
✗ Branch 1 not taken.
1085 ha_alter_info, m_tot_parts, m_part_share, this->table, altered_table);
10328 1085 ha_alter_info->handler_trivial_ctx =
10329 1085 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE);
10330
10331
3/4
✓ Branch 0 taken 827 times.
✓ Branch 1 taken 255 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
1085 switch (instant_type) {
10332 827 case Instant_Type::INSTANT_IMPOSSIBLE:
10333 827 break;
10334 255 case Instant_Type::INSTANT_ADD_DROP_COLUMN:
10335
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 248 times.
255 if (ha_alter_info->alter_info->requested_algorithm ==
10336 Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) {
10337 7 break;
10338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 } else if (m_prebuilt->table->n_def == REC_MAX_N_FIELDS) {
10339 if (ha_alter_info->alter_info->requested_algorithm ==
10340 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10341 my_error(ER_TOO_MANY_FIELDS, MYF(0), m_prebuilt->table->name.m_name);
10342 return HA_ALTER_ERROR;
10343 }
10344 /* INSTANT can't be done any more. Fall back to INPLACE. */
10345 break;
10346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 } else if (m_prebuilt->table->current_row_version == MAX_ROW_VERSION) {
10347 if (ha_alter_info->alter_info->requested_algorithm ==
10348 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10349 my_error(ER_INNODB_MAX_ROW_VERSION, MYF(0),
10350 m_prebuilt->table->name.m_name);
10351 return HA_ALTER_ERROR;
10352 }
10353 /* INSTANT can't be done any more. Fall back to INPLACE. */
10354 break;
10355
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 244 times.
248 } else if (!Instant_ddl_impl<dd::Table>::is_instant_add_possible(
10356
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
248 ha_alter_info, table, altered_table, m_prebuilt->table)) {
10357
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (ha_alter_info->alter_info->requested_algorithm ==
10358 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10359 /* Try to find if after adding columns, any possible row stays
10360 within permissible limit. If it doesn't, return error. */
10361
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 my_error(ER_INNODB_INSTANT_ADD_NOT_SUPPORTED_MAX_SIZE, MYF(0));
10362 4 return HA_ALTER_ERROR;
10363 }
10364
10365 /* INSTANT can't be done. Fall back to INPLACE. */
10366 break;
10367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
244 } else if (ha_alter_info->error_if_not_empty) {
10368 /* In this case, it can't be instant because the table
10369 may not be empty. Have to fall back to INPLACE */
10370 break;
10371 }
10372 [[fallthrough]];
10373 case Instant_Type::INSTANT_NO_CHANGE:
10374 case Instant_Type::INSTANT_VIRTUAL_ONLY:
10375 case Instant_Type::INSTANT_COLUMN_RENAME:
10376
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 246 times.
247 if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
10377 /* Deny the inplace ALTER TABLE. MySQL will try to
10378 re-create the table and ha_innobase::create() will
10379 return an error too. This is how we effectively
10380 deny adding too many columns to a table. */
10381 1 ha_alter_info->unsupported_reason =
10382
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 innobase_get_err_msg(ER_TOO_MANY_FIELDS);
10383 1 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10384 }
10385
10386 246 ha_alter_info->handler_trivial_ctx = instant_type_to_int(instant_type);
10387 246 return HA_ALTER_INPLACE_INSTANT;
10388 }
10389
10390 /* Check for PK and UNIQUE should already be done when creating the
10391 new table metadata.
10392 (fix_partition_info/check_primary_key+check_unique_key) */
10393
10394
1/2
✓ Branch 0 taken 834 times.
✗ Branch 1 not taken.
834 set_partition(0);
10395
1/2
✓ Branch 0 taken 834 times.
✗ Branch 1 not taken.
834 return ha_innobase::check_if_supported_inplace_alter(altered_table,
10396 834 ha_alter_info);
10397 2064 }
10398
10399 /** Prepare in-place ALTER for table.
10400 Allows InnoDB to update internal structures with concurrent
10401 writes blocked (provided that check_if_supported_inplace_alter()
10402 did not return HA_ALTER_INPLACE_NO_LOCK).
10403 This will be invoked before inplace_alter_table().
10404 @param[in] altered_table TABLE object for new version of table.
10405 @param[in,out] ha_alter_info Structure describing changes to be done
10406 by ALTER TABLE and holding data used during in-place alter.
10407 @param[in] old_table_def dd::Table object describing old
10408 version of the table.
10409 @param[in,out] new_table_def dd::Table object for the new version
10410 of the table. Can be adjusted by this call. Changes to the table
10411 definition will be persisted in the data-dictionary at statement
10412 commit time.
10413 @retval true Failure.
10414 @retval false Success. */
10415 1985 bool ha_innopart::prepare_inplace_alter_table(TABLE *altered_table,
10416 Alter_inplace_info *ha_alter_info,
10417 const dd::Table *old_table_def,
10418 dd::Table *new_table_def) {
10419
1/2
✓ Branch 0 taken 1985 times.
✗ Branch 1 not taken.
1985 DBUG_TRACE;
10420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1985 times.
1985 assert(ha_alter_info->handler_ctx == nullptr);
10421
10422
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1983 times.
1985 if (tablespace_is_shared_space(ha_alter_info->create_info)) {
10423
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION, PARTITION_IN_SHARED_TABLESPACE,
10424 MYF(0));
10425 2 return true;
10426 }
10427
10428 /* The row format in new table may differ from the old one,
10429 which is set by server earlier. So keep them the same */
10430
2/4
✓ Branch 0 taken 1983 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1983 times.
✗ Branch 3 not taken.
1983 new_table_def->set_row_format(old_table_def->row_format());
10431
10432
2/2
✓ Branch 0 taken 554 times.
✓ Branch 1 taken 1429 times.
1983 if (altered_table->found_next_number_field != nullptr) {
10433
2/4
✓ Branch 0 taken 554 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 554 times.
✗ Branch 3 not taken.
554 dd_copy_autoinc(old_table_def->se_private_data(),
10434
1/2
✓ Branch 0 taken 554 times.
✗ Branch 1 not taken.
554 new_table_def->se_private_data());
10435 }
10436
10437
2/2
✓ Branch 0 taken 943 times.
✓ Branch 1 taken 1040 times.
1983 if (alter_parts::apply_to(ha_alter_info)) {
10438
1/2
✓ Branch 0 taken 868 times.
✗ Branch 1 not taken.
943 return prepare_inplace_alter_partition(altered_table, ha_alter_info,
10439 868 old_table_def, new_table_def);
10440 }
10441
10442 ha_innopart_inplace_ctx *ctx_parts;
10443
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 THD *thd = ha_thd();
10444 1040 bool res = true;
10445
10446 /* Clean up all ins/upd nodes. */
10447
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 clear_ins_upd_nodes();
10448 /*
10449 This object will be freed by server, so always use 'new'
10450 and there is no need to free on failure */
10451
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 ctx_parts = new (thd->mem_root) ha_innopart_inplace_ctx(m_tot_parts);
10452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1040 times.
1040 if (ctx_parts == nullptr) {
10453 return HA_ALTER_ERROR;
10454 }
10455
10456
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 ctx_parts->ctx_array = ut::new_arr_withkey<inplace_alter_handler_ctx *>(
10457 1040 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts + 1});
10458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1040 times.
1040 if (ctx_parts->ctx_array == nullptr) {
10459 return HA_ALTER_ERROR;
10460 }
10461
10462 1040 memset(ctx_parts->ctx_array, 0,
10463 1040 sizeof(inplace_alter_handler_ctx *) * (m_tot_parts + 1));
10464
10465
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 ctx_parts->m_old_info = ut::new_arr_withkey<alter_table_old_info_t>(
10466 1040 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts});
10467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1040 times.
1040 if (ctx_parts->m_old_info == nullptr) {
10468 return HA_ALTER_ERROR;
10469 }
10470
10471
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 ctx_parts->prebuilt_array = ut::new_arr_withkey<row_prebuilt_t *>(
10472 1040 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts});
10473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1040 times.
1040 if (ctx_parts->prebuilt_array == nullptr) {
10474 return HA_ALTER_ERROR;
10475 }
10476 /* For the first partition use the current prebuilt. */
10477 1040 ctx_parts->prebuilt_array[0] = m_prebuilt;
10478 /* Create new prebuilt for the rest of the partitions.
10479 It is needed for the current implementation of
10480 ha_innobase::commit_inplace_alter_table(). */
10481
2/2
✓ Branch 0 taken 3365 times.
✓ Branch 1 taken 1040 times.
4405 for (uint i = 1; i < m_tot_parts; i++) {
10482 row_prebuilt_t *tmp_prebuilt;
10483
1/2
✓ Branch 0 taken 3365 times.
✗ Branch 1 not taken.
3365 tmp_prebuilt = row_create_prebuilt(m_part_share->get_table_part(i),
10484
1/2
✓ Branch 0 taken 3365 times.
✗ Branch 1 not taken.
3365 table_share->reclength);
10485 /* Use same trx as original prebuilt. */
10486 3365 tmp_prebuilt->trx = m_prebuilt->trx;
10487 3365 ctx_parts->prebuilt_array[i] = tmp_prebuilt;
10488 }
10489
10490
2/2
✓ Branch 0 taken 315 times.
✓ Branch 1 taken 725 times.
1040 if (altered_table->found_next_number_field != nullptr) {
10491
1/2
✓ Branch 0 taken 315 times.
✗ Branch 1 not taken.
315 dd_set_autoinc(new_table_def->se_private_data(),
10492
1/2
✓ Branch 0 taken 315 times.
✗ Branch 1 not taken.
315 ha_alter_info->create_info->auto_increment_value);
10493 }
10494
10495 1040 const char *save_tablespace = ha_alter_info->create_info->tablespace;
10496
10497 1040 const char *save_data_file_name = ha_alter_info->create_info->data_file_name;
10498
10499
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 auto oldp = old_table_def->leaf_partitions().begin();
10500
1/2
✓ Branch 0 taken 1040 times.
✗ Branch 1 not taken.
1040 auto newp = new_table_def->leaf_partitions()->begin();
10501
10502
2/2
✓ Branch 0 taken 4311 times.
✓ Branch 1 taken 946 times.
5257 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10503 4311 m_prebuilt = ctx_parts->prebuilt_array[i];
10504
1/2
✓ Branch 0 taken 4311 times.
✗ Branch 1 not taken.
4311 set_partition(i);
10505
10506 4311 const dd::Partition *old_part = *oldp;
10507 4311 dd::Partition *new_part = *newp;
10508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4311 times.
4311 ut_ad(old_part != nullptr);
10509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4311 times.
4311 ut_ad(new_part != nullptr);
10510
10511 /* We exempt this asserion when we do inplace during copy algorithm (ie.
10512 during expanded fast index creation). This is OK because we are using an
10513 intermediate table created during ALTER COPY algorithm. Hence
10514 m_prebuilt->table->id is newer than the original id stored in DD */
10515
3/8
✓ Branch 0 taken 4311 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4311 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4311 times.
4311 ut_ad(m_prebuilt->table->id == old_part->se_private_id() ||
10516 m_prebuilt->table->skip_alter_undo);
10517
10518 4311 ha_alter_info->handler_ctx = nullptr;
10519
10520 /* Set the tablespace and data_file_name value of the
10521 alter_info to the tablespace and data_file_name value
10522 that was existing for the partition originally, so that
10523 for ALTER TABLE the tablespace clause in create option
10524 is ignored for existing partitions, and later set it
10525 back to its old value */
10526
10527 4311 ha_alter_info->create_info->tablespace = m_prebuilt->table->tablespace;
10528 4311 ha_alter_info->create_info->data_file_name =
10529 4311 m_prebuilt->table->data_dir_path;
10530
10531
1/2
✓ Branch 0 taken 4221 times.
✗ Branch 1 not taken.
4311 res = prepare_inplace_alter_table_impl<dd::Partition>(
10532 altered_table, ha_alter_info, old_part, new_part);
10533
10534
1/2
✓ Branch 0 taken 4221 times.
✗ Branch 1 not taken.
4221 update_partition(i);
10535 4221 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10536
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4217 times.
4221 if (res) {
10537 4 break;
10538 }
10539
10540 4217 ha_innobase_inplace_ctx *ctx =
10541 4217 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10542
2/2
✓ Branch 0 taken 3150 times.
✓ Branch 1 taken 1067 times.
4217 if (ctx != nullptr) {
10543 3150 ctx_parts->m_old_info[i].update(ctx->old_table, ctx->need_rebuild());
10544 }
10545
10546 4217 ++i;
10547 }
10548
10549 950 m_prebuilt = ctx_parts->prebuilt_array[0];
10550 950 ha_alter_info->handler_ctx = ctx_parts;
10551 950 ha_alter_info->group_commit_ctx = ctx_parts->ctx_array;
10552 950 ha_alter_info->create_info->tablespace = save_tablespace;
10553 950 ha_alter_info->create_info->data_file_name = save_data_file_name;
10554
10555 950 return res;
10556 1820 }
10557
10558 1801 bool ha_innopart::inplace_alter_table(TABLE *altered_table,
10559 Alter_inplace_info *ha_alter_info,
10560 const dd::Table *old_table_def,
10561 dd::Table *new_table_def) {
10562
2/2
✓ Branch 0 taken 855 times.
✓ Branch 1 taken 946 times.
1801 if (alter_parts::apply_to(ha_alter_info)) {
10563
1/2
✓ Branch 0 taken 855 times.
✗ Branch 1 not taken.
855 return (inplace_alter_partition(ha_alter_info));
10564 }
10565
10566 946 bool res = true;
10567 ha_innopart_inplace_ctx *ctx_parts;
10568
10569 946 ctx_parts =
10570 static_cast<ha_innopart_inplace_ctx *>(ha_alter_info->handler_ctx);
10571
10572 /* It could be not allocated at all */
10573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 946 times.
946 if (ctx_parts == nullptr) {
10574 return (false);
10575 }
10576
10577
1/2
✓ Branch 0 taken 946 times.
✗ Branch 1 not taken.
946 auto oldp = old_table_def->leaf_partitions().begin();
10578
1/2
✓ Branch 0 taken 946 times.
✗ Branch 1 not taken.
946 auto newp = new_table_def->leaf_partitions()->begin();
10579
10580
2/2
✓ Branch 0 taken 4127 times.
✓ Branch 1 taken 944 times.
5071 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10581 4127 m_prebuilt = ctx_parts->prebuilt_array[i];
10582 4127 ha_alter_info->handler_ctx = ctx_parts->ctx_array[i];
10583
1/2
✓ Branch 0 taken 4127 times.
✗ Branch 1 not taken.
4127 set_partition(i);
10584
4/4
✓ Branch 0 taken 3181 times.
✓ Branch 1 taken 946 times.
✓ Branch 2 taken 2360 times.
✓ Branch 3 taken 821 times.
4127 if (i != 0 && ha_alter_info->handler_ctx != nullptr) {
10585
1/2
✓ Branch 0 taken 2360 times.
✗ Branch 1 not taken.
2360 ha_alter_info->handler_ctx->set_shared_data(ctx_parts->ctx_array[i - 1]);
10586 }
10587
10588
1/2
✓ Branch 0 taken 4127 times.
✗ Branch 1 not taken.
4127 res = inplace_alter_table_impl<dd::Partition>(altered_table, ha_alter_info);
10589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4127 times.
4127 ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx);
10590 4127 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10591
10592
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4125 times.
4127 if (res) {
10593 2 break;
10594 }
10595
10596 4125 ++i;
10597 }
10598 946 m_prebuilt = ctx_parts->prebuilt_array[0];
10599 946 ha_alter_info->handler_ctx = ctx_parts;
10600 946 return (res);
10601 }
10602
10603 /** Commit or rollback.
10604 Commit or rollback the changes made during
10605 prepare_inplace_alter_table() and inplace_alter_table() inside
10606 the storage engine. Note that the allowed level of concurrency
10607 during this operation will be the same as for
10608 inplace_alter_table() and thus might be higher than during
10609 prepare_inplace_alter_table(). (E.g concurrent writes were
10610 blocked during prepare, but might not be during commit).
10611 @param[in] altered_table TABLE object for new version of table.
10612 @param[in,out] ha_alter_info Structure describing changes to be done
10613 by ALTER TABLE and holding data used during
10614 in-place alter.
10615 @param[in] commit true => Commit, false => Rollback.
10616 @param[in] old_table_def dd::Table object describing old
10617 version of the table.
10618 @param[in,out] new_table_def dd::Table object for the new version
10619 of the table. Can be adjusted by this call. Changes to the table
10620 definition will be persisted in the data-dictionary at statement
10621 commit time.
10622 @retval true Failure.
10623 @retval false Success. */
10624 1820 bool ha_innopart::commit_inplace_alter_table(TABLE *altered_table,
10625 Alter_inplace_info *ha_alter_info,
10626 bool commit,
10627 const dd::Table *old_table_def,
10628 dd::Table *new_table_def) {
10629
2/2
✓ Branch 0 taken 868 times.
✓ Branch 1 taken 952 times.
1820 if (alter_parts::apply_to(ha_alter_info)) {
10630 868 return (commit_inplace_alter_partition(altered_table, ha_alter_info, commit,
10631 818 old_table_def, new_table_def));
10632 }
10633
10634 952 ha_innopart_inplace_ctx *ctx_parts =
10635 static_cast<ha_innopart_inplace_ctx *>(ha_alter_info->handler_ctx);
10636
10637 /* It could be not allocated at all */
10638
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 950 times.
952 if (ctx_parts == nullptr) {
10639 2 return (false);
10640 }
10641
10642 950 bool res = false;
10643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 950 times.
950 ut_ad(ctx_parts->ctx_array != nullptr);
10644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 950 times.
950 ut_ad(ctx_parts->prebuilt_array != nullptr);
10645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 950 times.
950 ut_ad(ctx_parts->prebuilt_array[0] == m_prebuilt);
10646
10647
2/2
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 6 times.
950 if (commit) {
10648 /* Commit is done through first partition (group commit). */
10649
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 944 times.
944 ut_ad(ha_alter_info->group_commit_ctx == ctx_parts->ctx_array);
10650 944 ha_alter_info->handler_ctx = ctx_parts->ctx_array[0];
10651 944 set_partition(0);
10652
10653 944 res = ha_innobase::commit_inplace_alter_table_impl<dd::Table>(
10654 altered_table, ha_alter_info, commit, new_table_def);
10655
3/6
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 883 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 883 times.
883 ut_ad(res || !ha_alter_info->group_commit_ctx);
10656
10657 883 goto end;
10658 }
10659
10660 /* Rollback is done for each partition. */
10661
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (uint i = 0; i < m_tot_parts; i++) {
10662 12 m_prebuilt = ctx_parts->prebuilt_array[i];
10663 12 ha_alter_info->handler_ctx = ctx_parts->ctx_array[i];
10664 12 set_partition(i);
10665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ha_innobase::commit_inplace_alter_table_impl<dd::Table>(
10666 altered_table, ha_alter_info, commit, new_table_def)) {
10667 res = true;
10668 }
10669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx);
10670 12 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10671 }
10672 6 end:
10673 /* All are done successfully, now write back metadata to DD */
10674
3/4
✓ Branch 0 taken 883 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 883 times.
✗ Branch 3 not taken.
889 if (commit && !res) {
10675
4/6
✓ Branch 0 taken 246 times.
✓ Branch 1 taken 637 times.
✓ Branch 2 taken 246 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 883 times.
883 ut_ad(!(is_instant(ha_alter_info) && ctx_parts->m_old_info[0].m_rebuild));
10676
10677
1/2
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
883 auto oldp = old_table_def->leaf_partitions().begin();
10678
1/2
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
883 auto newp = new_table_def->leaf_partitions()->begin();
10679 883 bool inplace_instant = false;
10680
10681
2/2
✓ Branch 0 taken 3940 times.
✓ Branch 1 taken 883 times.
4823 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10682 3940 const dd::Partition *old_part = *oldp;
10683 3940 dd::Partition *new_part = *newp;
10684
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3940 times.
3940 ut_ad(old_part != nullptr);
10685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3940 times.
3940 ut_ad(new_part != nullptr);
10686
10687 3940 ha_innobase_inplace_ctx *ctx =
10688 3940 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10689
10690
2/2
✓ Branch 0 taken 1067 times.
✓ Branch 1 taken 2873 times.
3940 if (is_instant(ha_alter_info)) {
10691 Instant_ddl_impl<dd::Partition> executor(
10692 1067 ha_alter_info, m_user_thd, m_prebuilt->trx,
10693 1067 m_part_share->get_table_part(i), table, altered_table, old_part,
10694 new_part,
10695 1067 altered_table->found_next_number_field != nullptr
10696 57 ? reinterpret_cast<uint64_t *>(&m_part_share->next_auto_inc_val)
10697
3/4
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 1010 times.
✓ Branch 2 taken 1067 times.
✗ Branch 3 not taken.
1067 : nullptr);
10698
10699 /* Execute Instant DDL */
10700
1/2
✓ Branch 0 taken 1067 times.
✗ Branch 1 not taken.
1067 executor.commit_instant_ddl();
10701
2/4
✓ Branch 0 taken 2873 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2873 times.
3940 } else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
10702 ctx == nullptr) {
10703 dd_commit_inplace_no_change(ha_alter_info, old_part, new_part, true);
10704 } else {
10705 2873 inplace_instant = !ctx_parts->m_old_info[0].m_rebuild;
10706
10707 /* Table is not rebuilt so copy instant metadata.
10708 NOTE : To be done only for first partition */
10709
4/4
✓ Branch 0 taken 637 times.
✓ Branch 1 taken 2236 times.
✓ Branch 2 taken 130 times.
✓ Branch 3 taken 507 times.
2873 if (i == 0 && inplace_instant) {
10710 130 dd_inplace_alter_copy_instant_metadata(
10711
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 ha_alter_info, &old_part->table(),
10712
2/4
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
130 const_cast<dd::Table *>(&new_part->table()));
10713 }
10714
10715
1/2
✓ Branch 0 taken 2873 times.
✗ Branch 1 not taken.
2873 dd_commit_inplace_alter_table(ctx_parts->m_old_info[i], ctx->new_table,
10716 old_part, new_part);
10717 }
10718
10719 3940 ++i;
10720 }
10721
10722 #ifdef UNIV_DEBUG
10723
1/2
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
883 if (!res) {
10724
5/6
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
✓ Branch 3 taken 690 times.
✓ Branch 4 taken 193 times.
✓ Branch 5 taken 690 times.
1076 if (dd_table_has_instant_cols(*old_table_def) &&
10725
1/2
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
193 !ctx_parts->m_old_info[0].m_rebuild) {
10726
2/4
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 193 times.
193 ut_ad(dd_table_has_instant_cols(*new_table_def));
10727 }
10728
10729 883 uint i = 0;
10730
3/4
✓ Branch 0 taken 883 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3940 times.
✓ Branch 3 taken 883 times.
4823 for (auto part : *new_table_def->leaf_partitions()) {
10731 ha_innobase_inplace_ctx *ctx =
10732 3940 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i++]);
10733
2/2
✓ Branch 0 taken 2873 times.
✓ Branch 1 taken 1067 times.
3940 if (ctx != nullptr) {
10734
2/4
✓ Branch 0 taken 2873 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2873 times.
2873 ut_ad(dd_table_match(ctx->new_table, part));
10735 }
10736 }
10737 }
10738 #endif /* univ_debug */
10739 }
10740
10741 /* Move the ownership of the new tables back to the m_part_share. */
10742 ha_innobase_inplace_ctx *ctx;
10743
2/2
✓ Branch 0 taken 3127 times.
✓ Branch 1 taken 639 times.
3766 for (uint i = 0; i < m_tot_parts; i++) {
10744 /* TODO: Fix to only use one prebuilt (i.e. make inplace
10745 alter partition aware instead of using multiple prebuilt
10746 copies... */
10747 3127 ctx = static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10748
2/2
✓ Branch 0 taken 2877 times.
✓ Branch 1 taken 250 times.
3127 if (ctx != nullptr) {
10749 2877 m_part_share->set_table_part(i, ctx->prebuilt->table);
10750 2877 ctx->prebuilt->table = nullptr;
10751 2877 ctx_parts->prebuilt_array[i] = ctx->prebuilt;
10752 } else {
10753 250 break;
10754 }
10755 }
10756 /* The above juggling of prebuilt must be reset here. */
10757 889 m_prebuilt = ctx_parts->prebuilt_array[0];
10758 889 m_prebuilt->table = m_part_share->get_table_part(0);
10759 889 ha_alter_info->handler_ctx = ctx_parts;
10760 889 return (res);
10761 }
10762
10763 /** Create the Altered_partitoins object
10764 @param[in] ha_alter_info thd DDL operation
10765 @retval true On failure
10766 @retval false On success */
10767 593 bool ha_innopart::prepare_for_copy_partitions(
10768 Alter_inplace_info *ha_alter_info) {
10769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 593 times.
593 ut_ad(m_new_partitions == nullptr);
10770
2/4
✓ Branch 0 taken 593 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 593 times.
593 ut_ad(alter_parts::need_copy(ha_alter_info));
10771
10772 593 uint num_parts = ha_alter_info->modified_part_info->num_parts;
10773 593 uint total_parts = num_parts;
10774
10775
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 527 times.
593 if (ha_alter_info->modified_part_info->is_sub_partitioned()) {
10776 66 total_parts *= ha_alter_info->modified_part_info->num_subparts;
10777 }
10778
10779
1/2
✓ Branch 0 taken 593 times.
✗ Branch 1 not taken.
593 m_new_partitions = ut::new_withkey<Altered_partitions>(
10780 ut::make_psi_memory_key(mem_key_partitioning), total_parts);
10781
10782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 593 times.
593 if (m_new_partitions == nullptr) {
10783 return (true);
10784
2/4
✓ Branch 0 taken 593 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 593 times.
593 } else if (m_new_partitions->initialize()) {
10785 ut::delete_(m_new_partitions);
10786 m_new_partitions = nullptr;
10787 return (true);
10788 }
10789
10790 593 return (false);
10791 }
10792
10793 /** write row to new partition.
10794 @param[in] new_part New partition to write to.
10795 @return 0 for success else error code. */
10796 2687 int ha_innopart::write_row_in_new_part(uint new_part) {
10797 int result;
10798
1/2
✓ Branch 0 taken 2687 times.
✗ Branch 1 not taken.
2687 DBUG_TRACE;
10799
10800 2687 m_last_part = new_part;
10801
3/4
✓ Branch 0 taken 2687 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 2682 times.
2687 if (m_new_partitions->part(new_part) == nullptr) {
10802 /* Altered partition contains misplaced row. */
10803 5 m_err_rec = table->record[0];
10804 5 return HA_ERR_ROW_IN_WRONG_PARTITION;
10805 }
10806
10807
1/2
✓ Branch 0 taken 2682 times.
✗ Branch 1 not taken.
2682 m_new_partitions->prepare_write(m_prebuilt, new_part);
10808
1/2
✓ Branch 0 taken 2682 times.
✗ Branch 1 not taken.
2682 result = ha_innobase::write_row(table->record[0]);
10809
1/2
✓ Branch 0 taken 2682 times.
✗ Branch 1 not taken.
2682 m_new_partitions->finish_write(m_prebuilt, new_part);
10810 2682 return result;
10811 2687 }
10812
10813 /** Allows InnoDB to update internal structures with concurrent
10814 writes blocked (given that check_if_supported_inplace_alter()
10815 did not return HA_ALTER_INPLACE_NO_LOCK).
10816 This is for 'ALTER TABLE ... PARTITION' and a corresponding function
10817 to inplace_alter_table().
10818 This will be invoked before inplace_alter_partition().
10819
10820 @param[in,out] altered_table TABLE object for new version of table
10821 @param[in,out] ha_alter_info Structure describing changes to be done
10822 by ALTER TABLE and holding data used during
10823 in-place alter.
10824 @param[in] old_dd_tab Table definition before the ALTER
10825 @param[in,out] new_dd_tab Table definition after the ALTER
10826 @retval true Failure
10827 @retval false Success */
10828 943 bool ha_innopart::prepare_inplace_alter_partition(
10829 TABLE *altered_table, Alter_inplace_info *ha_alter_info,
10830 const dd::Table *old_dd_tab, dd::Table *new_dd_tab) {
10831 943 clear_ins_upd_nodes();
10832
10833 943 trx_start_if_not_started_xa(m_prebuilt->trx, true, UT_LOCATION_HERE);
10834
10835
3/4
✓ Branch 0 taken 593 times.
✓ Branch 1 taken 350 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 943 times.
1536 if (alter_parts::need_copy(ha_alter_info) &&
10836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 593 times.
593 prepare_for_copy_partitions(ha_alter_info)) {
10837 my_error(ER_OUT_OF_RESOURCES, MYF(0));
10838 return (true);
10839 }
10840
10841 943 alter_parts *ctx = ut::new_withkey<alter_parts>(
10842 943 UT_NEW_THIS_FILE_PSI_KEY, m_prebuilt->trx, m_part_share, ha_alter_info,
10843 943 m_part_info, m_new_partitions);
10844
10845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 943 times.
943 if (ctx == nullptr) {
10846 my_error(ER_OUT_OF_RESOURCES, MYF(0));
10847 return (true);
10848 }
10849
10850 943 ha_alter_info->handler_ctx = ctx;
10851
10852 943 int error = ctx->prepare(*old_dd_tab, *new_dd_tab, altered_table);
10853
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 855 times.
868 if (error != 0) {
10854
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
10855 }
10856 868 return (error);
10857 }
10858
10859 855 bool ha_innopart::inplace_alter_partition(Alter_inplace_info *ha_alter_info) {
10860
3/4
✓ Branch 0 taken 855 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 332 times.
✓ Branch 3 taken 523 times.
855 if (!alter_parts::need_copy(ha_alter_info)) {
10861 332 return (false);
10862 }
10863
10864 /* The lock type can be set as none, since in this step, the
10865 shared table lock is held, thus no other changes. This is to fix
10866 if the table was explicitly lock, then select_lock_type in the
10867 prebuilt here would not be LOCK_NONE, then row locks would be
10868 required; if we finally want to drop the original partitions,
10869 these row locks would lead to failure/crash. */
10870 523 ulint lock_type = m_prebuilt->select_lock_type;
10871 523 m_prebuilt->select_lock_type = LOCK_NONE;
10872
10873
1/2
✓ Branch 0 taken 523 times.
✗ Branch 1 not taken.
523 prepare_change_partitions();
10874
10875 523 partition_info *old_part_info = table->part_info;
10876
10877
1/2
✓ Branch 0 taken 523 times.
✗ Branch 1 not taken.
523 set_part_info(ha_alter_info->modified_part_info, true);
10878
10879
1/2
✓ Branch 0 taken 523 times.
✗ Branch 1 not taken.
523 prepare_change_partitions();
10880
10881 ulonglong deleted;
10882 int res;
10883
10884
1/2
✓ Branch 0 taken 523 times.
✗ Branch 1 not taken.
523 res = copy_partitions(&deleted);
10885
10886
1/2
✓ Branch 0 taken 523 times.
✗ Branch 1 not taken.
523 set_part_info(old_part_info, false);
10887
10888 523 m_prebuilt->select_lock_type = lock_type;
10889
10890
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 494 times.
523 if (res > 0) {
10891
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 print_error(res, MYF(res != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
10892 }
10893
10894 523 return (res);
10895 }
10896
10897 /** Prepare to commit or roll back ALTER TABLE...ALGORITHM=INPLACE.
10898 This is for 'ALTER TABLE ... PARTITION' and a corresponding function
10899 to commit_inplace_alter_table().
10900 @param[in,out] altered_table TABLE object for new version of table.
10901 @param[in,out] ha_alter_info ALGORITHM=INPLACE metadata
10902 @param[in] commit true=Commit, false=Rollback.
10903 @param[in] old_dd_tab old table
10904 @param[in,out] new_dd_tab new table
10905 @retval true on failure (my_error() will have been called)
10906 @retval false on success */
10907 868 bool ha_innopart::commit_inplace_alter_partition(
10908 TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit,
10909 const dd::Table *old_dd_tab, dd::Table *new_dd_tab) {
10910 868 alter_parts *ctx = static_cast<alter_parts *>(ha_alter_info->handler_ctx);
10911 868 m_prebuilt->table = nullptr;
10912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 868 times.
868 if (ctx == nullptr) {
10913 ut_ad(!commit);
10914 return (false);
10915 }
10916
10917
2/2
✓ Branch 0 taken 826 times.
✓ Branch 1 taken 42 times.
868 if (commit) {
10918 826 int error = ctx->try_commit(*old_dd_tab, *new_dd_tab, table, altered_table);
10919
1/2
✓ Branch 0 taken 776 times.
✗ Branch 1 not taken.
776 if (!error) {
10920 776 ut::delete_(ctx);
10921 776 ha_alter_info->handler_ctx = nullptr;
10922
10923 776 ut::delete_(m_new_partitions);
10924 776 m_new_partitions = nullptr;
10925
10926
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 662 times.
776 if (altered_table->found_next_number_field) {
10927 114 dd_set_autoinc(new_dd_tab->se_private_data(),
10928 114 m_part_share->next_auto_inc_val);
10929 }
10930
10931 776 dd_copy_table(ha_alter_info, *new_dd_tab, *old_dd_tab);
10932 776 dd_part_adjust_table_id(new_dd_tab);
10933
10934
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 739 times.
776 if (dd_table_has_instant_cols(*old_dd_tab)) {
10935 37 dd_inplace_alter_copy_instant_metadata(ha_alter_info, old_dd_tab,
10936 new_dd_tab);
10937 }
10938 }
10939
10940 776 return (error != 0);
10941 }
10942
10943 42 ctx->rollback();
10944 42 ut::delete_(ctx);
10945 42 ha_alter_info->handler_ctx = nullptr;
10946
10947 42 ut::delete_(m_new_partitions);
10948 42 m_new_partitions = nullptr;
10949
10950 42 return (false);
10951 }
10952
10953 /** Check if the DATA DIRECTORY is specified (implicitly or explicitly)
10954 @param[in] dd_part The dd::Partition to be checked
10955 @retval true the DATA DIRECTORY is specified (implicitly or explicitly)
10956 @retval false otherwise */
10957 203 static bool dd_part_has_datadir(const dd::Partition *dd_part) {
10958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 ut_ad(dd_part_is_stored(dd_part));
10959
10960
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
203 return (dd_part->options().exists(data_file_name_key) ||
10961
3/4
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 109 times.
175 (dd_part->parent() != nullptr &&
10962
7/12
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 66 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 66 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 66 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 66 times.
✗ Branch 11 not taken.
647 dd_part->parent()->options().exists(data_file_name_key)) ||
10963
4/10
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 175 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 175 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
553 dd_part->table().se_private_data().exists(
10964
3/4
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
✓ Branch 3 taken 28 times.
581 dd_table_key_strings[DD_TABLE_DATA_DIRECTORY]));
10965 }
10966
10967 /** Adjust data directory for exchange partition. Special handling of
10968 dict_table_t::data_dir_path is necessary if DATA DIRECTORY is specified. For
10969 exaple if DATA DIRECTORY Is '/tmp', the data directory for nomral table is
10970 '/tmp/t1', while for partition is '/tmp'. So rename, the postfix table name 't1'
10971 should either be truncated or appended.
10972 @param[in] table_p partiton table
10973 @param[in] table_s swap table*/
10974 28 void exchange_partition_adjust_datadir(dict_table_t *table_p,
10975 dict_table_t *table_s) {
10976
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 ut_ad(table_s->n_ref_count == 1);
10977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 ut_ad(table_p->n_ref_count == 1);
10978
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (table_s->data_dir_path != nullptr) {
10979
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 std::string str(table_s->data_dir_path);
10980 /* new_name contains database/name but we require name */
10981 28 const char *name = strchr(table_s->name.m_name, '/') + 1;
10982
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 str.append(name);
10983
10984
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 ulint old_size = mem_heap_get_size(table_s->heap);
10985
10986
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 table_s->data_dir_path = mem_heap_strdup(table_s->heap, str.c_str());
10987
10988
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 ulint new_size = mem_heap_get_size(table_s->heap);
10989
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 dict_sys_mutex_enter();
10990 28 dict_sys->size += new_size - old_size;
10991
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 dict_sys_mutex_exit();
10992 28 }
10993
10994
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 17 times.
28 if (table_p->data_dir_path != nullptr) {
10995
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 std::string str(table_p->data_dir_path);
10996 11 size_t found = str.find_last_of("/\\");
10997
10998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 ut_ad(found != std::string::npos);
10999 11 found++;
11000
11001 11 table_p->data_dir_path[found] = '\0';
11002 11 }
11003 28 }
11004
11005 /** Exchange partition.
11006 Low-level primitive which implementation is provided here.
11007 @param[in] part_id The id of the partition to be exchanged
11008 @param[in] part_table partitioned table to be exchanged
11009 @param[in] swap_table table to be exchanged
11010 @return error number
11011 @retval 0 on success */
11012 212 int ha_innopart::exchange_partition_low(uint part_id, dd::Table *part_table,
11013 dd::Table *swap_table) {
11014
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 DBUG_TRACE;
11015
11016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(part_table != nullptr);
11017
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(swap_table != nullptr);
11018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(m_part_share != nullptr);
11019
2/4
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 212 times.
212 ut_ad(dd_table_is_partitioned(*part_table));
11020
2/4
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 212 times.
212 ut_ad(!dd_table_is_partitioned(*swap_table));
11021
3/6
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 212 times.
212 ut_ad(innobase_strcasecmp(part_table->name().c_str(),
11022 table_share->table_name.str) == 0);
11023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(part_id < m_tot_parts);
11024 212 std::vector<dd::Partition_index *> part_indexes;
11025 212 std::vector<dd::Partition_index *>::iterator p_iter;
11026 212 std::vector<dd::Index *> swap_indexes;
11027 212 std::vector<dd::Index *>::iterator s_iter;
11028 #ifdef UNIV_DEBUG
11029 212 std::vector<dd::Index *> part_table_indexes;
11030 212 std::vector<dd::Index *>::iterator pt_iter;
11031 #endif
11032
11033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 if (high_level_read_only) {
11034 my_error(ER_READ_ONLY_MODE, MYF(0));
11035 return HA_ERR_TABLE_READONLY;
11036 }
11037
11038
3/6
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 212 times.
424 if (dd_table_has_instant_cols(*part_table) ||
11039
2/4
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 212 times.
212 dd_table_has_instant_cols(*swap_table)) {
11040 my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
11041 "INSTANT COLUMN(s)");
11042 return true;
11043 }
11044
11045 /* Find the specified dd::Partition object */
11046 212 uint id = 0;
11047 212 dd::Partition *dd_part = nullptr;
11048
2/4
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 429 times.
✗ Branch 3 not taken.
429 for (auto part : *part_table->leaf_partitions()) {
11049
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
429 ut_d(dict_table_t *table = m_part_share->get_table_part(id));
11050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 429 times.
429 ut_ad(table->n_ref_count == 1);
11051
2/4
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 429 times.
429 ut_ad(!table->is_temporary());
11052
11053
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 217 times.
429 if (++id > part_id) {
11054 212 dd_part = part;
11055 212 break;
11056 }
11057 }
11058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(dd_part != nullptr);
11059
11060
4/8
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 212 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 212 times.
424 if (dd_part->options().exists(index_file_name_key) ||
11061
3/6
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 212 times.
212 swap_table->options().exists(index_file_name_key)) {
11062 my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), "INDEX DIRECTORY");
11063 ut_d(ut_error);
11064 ut_o(return true);
11065 }
11066
11067 /* Get the innodb table objects of part_table and swap_table */
11068
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 const table_id_t table_id = swap_table->se_private_id();
11069
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 dict_table_t *part = m_part_share->get_table_part(part_id);
11070 dict_table_t *swap;
11071 212 const auto hash_value = ut::hash_uint64(table_id);
11072
11073
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 dict_sys_mutex_enter();
11074
7/12
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 212 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 213 times.
✓ Branch 8 taken 212 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 213 times.
✗ Branch 11 not taken.
213 HASH_SEARCH(id_hash, dict_sys->table_id_hash, hash_value, dict_table_t *,
11075 swap, ut_ad(swap->cached), swap->id == table_id);
11076
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 dict_sys_mutex_exit();
11077
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(swap != nullptr);
11078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212 times.
212 ut_ad(swap->n_ref_count == 1);
11079
11080 #ifdef UNIV_DEBUG
11081 /* Store and sort part_table indexes */
11082
6/12
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 212 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 212 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 212 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 212 times.
✗ Branch 11 not taken.
212 std::copy(part_table->indexes()->begin(), part_table->indexes()->end(),
11083 std::back_inserter(part_table_indexes));
11084
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 std::sort(part_table_indexes.begin(), part_table_indexes.end(),
11085 99 [](dd::Index *a, dd::Index *b) { return (a->name() < b->name()); });
11086 #endif
11087
1/2
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
212 dd::Object_id p_se_id = dd_part->se_private_id();
11088
11089 /* Try to rename files. Tablespace checking ensures that
11090 both partition and table are of implicit tablespace. The plan is:
11091 1. Rename the swap table to the intermediate file
11092 2. Rename the partition to the swap table file
11093 3. Rename the intermediate file of swap table to the partition file */
11094 212 THD *thd = m_prebuilt->trx->mysql_thd;
11095 212 char *swap_name = strdup(swap->name.m_name);
11096 212 char *part_name = strdup(part->name.m_name);
11097
11098 /* Define the temporary table name, by appending TMP_POSTFIX */
11099 char temp_name[FN_REFLEN];
11100 212 snprintf(temp_name, sizeof temp_name, "%s%s", swap_name,
11101 dict_name::TMP_POSTFIX);
11102
11103 212 int error = 0;
11104
1/2
✓ Branch 0 taken 209 times.
✗ Branch 1 not taken.
212 error = innobase_basic_ddl::rename_impl<dd::Table>(
11105 thd, swap_name, temp_name, swap_table, swap_table, nullptr);
11106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 209 times.
209 if (error != 0) {
11107 goto func_exit;
11108 }
11109
1/2
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
209 error = innobase_basic_ddl::rename_impl<dd::Partition>(
11110 thd, part_name, swap_name, dd_part, dd_part, nullptr);
11111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (error != 0) {
11112 goto func_exit;
11113 }
11114
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
206 error = innobase_basic_ddl::rename_impl<dd::Table>(
11115 thd, temp_name, part_name, swap_table, swap_table, nullptr);
11116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 if (error != 0) {
11117 goto func_exit;
11118 }
11119
11120
5/6
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 28 times.
✓ Branch 5 taken 175 times.
378 if (dd_part_has_datadir(dd_part) ||
11121
3/6
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 175 times.
175 swap_table->options().exists(data_file_name_key)) {
11122 /* after above swaping swap is now partition table and part is now normal
11123 table */
11124
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 exchange_partition_adjust_datadir(swap, part);
11125 }
11126
11127
6/12
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 203 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 203 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 203 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 203 times.
✗ Branch 11 not taken.
203 std::copy(dd_part->indexes()->begin(), dd_part->indexes()->end(),
11128 std::back_inserter(part_indexes));
11129
6/12
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 203 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 203 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 203 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 203 times.
✗ Branch 11 not taken.
203 std::copy(swap_table->indexes()->begin(), swap_table->indexes()->end(),
11130 std::back_inserter(swap_indexes));
11131
11132 /* Sort the index pointers according to the index names because the index
11133 ordanility of the partition being exchanged may be different than the
11134 table being swapped */
11135
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
203 std::sort(part_indexes.begin(), part_indexes.end(),
11136 81 [](dd::Partition_index *a, dd::Partition_index *b) {
11137 81 return (a->name() < b->name());
11138 });
11139
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
203 std::sort(swap_indexes.begin(), swap_indexes.end(),
11140 79 [](dd::Index *a, dd::Index *b) { return (a->name() < b->name()); });
11141
11142 /* Swap the se_private_data and options between indexes.
11143 The se_private_data should be swapped between every index of
11144 dd_part and swap_table; however, options should be swapped(checked)
11145 between part_table and swap_table */
11146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
203 ut_ad(part_indexes.size() == swap_indexes.size());
11147 203 for (p_iter = part_indexes.begin(), s_iter = swap_indexes.begin();
11148
5/6
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 203 times.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✓ Branch 5 taken 203 times.
447 p_iter < part_indexes.end() && s_iter < swap_indexes.end();
11149 244 p_iter++, s_iter++) {
11150 244 auto part_index = *p_iter;
11151 244 auto swap_index = *s_iter;
11152
1/2
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
244 dd::Object_id p_tablespace_id = part_index->tablespace_id();
11153
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 part_index->set_tablespace_id(swap_index->tablespace_id());
11154
1/2
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
244 swap_index->set_tablespace_id(p_tablespace_id);
11155
11156
5/10
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 244 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 244 times.
244 ut_ad(part_index->se_private_data().empty() ==
11157 swap_index->se_private_data().empty());
11158
5/10
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 244 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 244 times.
244 ut_ad(part_index->se_private_data().size() ==
11159 swap_index->se_private_data().size());
11160
11161
3/6
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✗ Branch 5 not taken.
244 if (!part_index->se_private_data().empty()) {
11162 std::unique_ptr<dd::Properties> p_se_data(
11163
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 dd::Properties::parse_properties(""));
11164
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 p_se_data->insert_values(part_index->se_private_data());
11165
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 part_index->se_private_data().clear();
11166
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 part_index->set_se_private_data(swap_index->se_private_data());
11167
2/4
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 swap_index->se_private_data().clear();
11168
1/2
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
244 swap_index->set_se_private_data(*p_se_data);
11169 244 }
11170 }
11171 #ifdef UNIV_DEBUG
11172 203 for (s_iter = swap_indexes.begin(), pt_iter = part_table_indexes.begin();
11173
5/6
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 203 times.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✓ Branch 5 taken 203 times.
447 s_iter < swap_indexes.end() && pt_iter < part_table_indexes.end();
11174 244 pt_iter++, s_iter++) {
11175 244 auto part_table_index = *pt_iter;
11176 244 auto swap_index = *s_iter;
11177
11178
5/10
✓ Branch 0 taken 244 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 244 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 244 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 244 times.
244 ut_ad(part_table_index->options().raw_string() ==
11179 swap_index->options().raw_string());
11180 }
11181 #endif
11182
11183 /* Swap the se_private_data and options of the two tables.
11184 Only the max autoinc should be set to both tables */
11185
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 171 times.
203 if (m_part_share->get_table_share()->found_next_number_field) {
11186 32 uint64_t part_autoinc = part->autoinc;
11187 32 uint64_t swap_autoinc = swap->autoinc;
11188 32 uint64_t max_autoinc = std::max(part_autoinc, swap_autoinc);
11189
11190
2/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 dd_set_autoinc(swap_table->se_private_data(), max_autoinc);
11191
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 dd_set_autoinc(
11192
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 part_table->se_private_data(),
11193 32 std::max<uint64>(swap_autoinc, m_part_share->next_auto_inc_val));
11194
11195
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 dict_table_autoinc_lock(part);
11196
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 dict_table_autoinc_initialize(part, max_autoinc);
11197
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 dict_table_autoinc_unlock(part);
11198
11199
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 31 times.
32 if (m_part_share->next_auto_inc_val < swap_autoinc) {
11200
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 lock_auto_increment();
11201 1 m_part_share->next_auto_inc_val = swap_autoinc;
11202
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 unlock_auto_increment();
11203 }
11204 }
11205
11206 /* Swap the se_private_id between partition and table */
11207
11208
2/4
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
✗ Branch 3 not taken.
203 dd_part->set_se_private_id(swap_table->se_private_id());
11209
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
203 swap_table->set_se_private_id(p_se_id);
11210
11211
6/10
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 203 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 950 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 950 times.
✓ Branch 9 taken 203 times.
1153 for (auto dd_column : *swap_table->columns()) {
11212
3/6
✓ Branch 0 taken 950 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 950 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 950 times.
✗ Branch 5 not taken.
950 dd_column->se_private_data().set(dd_index_key_strings[DD_TABLE_ID],
11213 p_se_id);
11214 }
11215
11216
1/2
✓ Branch 0 taken 203 times.
✗ Branch 1 not taken.
203 dd_part_adjust_table_id(part_table);
11217
11218 203 func_exit:
11219 203 free(swap_name);
11220 203 free(part_name);
11221
11222 203 return error;
11223 203 }
11224